From c2044fbe09c83e62e422167686766d8edf0dade1 Mon Sep 17 00:00:00 2001 From: AngelCarpintero Date: Wed, 16 Jul 2008 20:34:23 +0000 Subject: [PATCH] Apply new code standard ( chuck 2 ) --- alg.c | 2298 ++++++++++++------------ alg.h | 36 +- conf.c | 3614 ++++++++++++++++++------------------- draw.c | 2281 ++++++++++++------------ event.c | 982 +++++----- event.h | 10 +- ffmpeg.c | 1047 +++++------ mmx.h | 422 ++--- motion.c | 4432 +++++++++++++++++++++++----------------------- netcam.c | 4476 +++++++++++++++++++++++----------------------- netcam_ftp.c | 1242 ++++++------- netcam_jpeg.c | 740 ++++---- netcam_wget.c | 364 ++-- picture.c | 1029 +++++------ rotate.c | 464 +++-- stream.c | 766 ++++---- track.c | 2086 +++++++++++----------- video.c | 1012 ++++++----- video.h | 72 +- video2.c | 1439 ++++++++------- video_common.c | 1340 +++++++------- video_freebsd.c | 1952 ++++++++++---------- webhttpd.c | 4524 +++++++++++++++++++++++------------------------ 23 files changed, 18444 insertions(+), 18184 deletions(-) diff --git a/alg.c b/alg.c index e22b510..c0ff6b3 100644 --- a/alg.c +++ b/alg.c @@ -1,9 +1,9 @@ -/* alg.c +/* alg.c * - * Detect changes in a video stream. - * Copyright 2001 by Jeroen Vreeken (pe1rxq@amsat.org) - * This software is distributed under the GNU public license version 2 - * See also the file 'COPYING'. + * Detect changes in a video stream. + * Copyright 2001 by Jeroen Vreeken (pe1rxq@amsat.org) + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. * */ #include "motion.h" @@ -20,348 +20,358 @@ /* locate the center and size of the movement. */ void alg_locate_center_size(struct images *imgs, int width, int height, struct coord *cent) { - unsigned char *out = imgs->out; - int *labels = imgs->labels; - int x, y, centc = 0, xdist = 0, ydist = 0; - - cent->x = 0; - cent->y = 0; - cent->maxx = 0; - cent->maxy = 0; - cent->minx = width; - cent->miny = height; - - /* If Labeling enabled - locate center of largest labelgroup */ - if (imgs->labelsize_max) { - /* Locate largest labelgroup */ - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - if (*(labels++) & 32768) { - cent->x += x; - cent->y += y; - centc++; - } - } - } - } else { - /* Locate movement */ - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - if (*(out++)) { - cent->x += x; - cent->y += y; - centc++; - } - } - } - } - if (centc) { - cent->x = cent->x / centc; - cent->y = cent->y / centc; - } - - /* Now we find the size of the Motion */ - - /* First reset pointers back to initial value */ - centc = 0; - labels = imgs->labels; - out = imgs->out; - - /* If Labeling then we find the area around largest labelgroup instead */ - if (imgs->labelsize_max) { - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - if (*(labels++) & 32768) { - if (x > cent->x) - xdist += x - cent->x; - else if (x < cent->x) - xdist += cent->x - x; - if (y > cent->y) - ydist += y - cent->y; - else if (y < cent->y) - ydist += cent->y - y; - centc++; - } - } - } - } else { - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - if (*(out++)) { - if (x > cent->x) - xdist += x - cent->x; - else if (x < cent->x) - xdist += cent->x - x; - if (y > cent->y) - ydist += y - cent->y; - else if (y < cent->y) - ydist += cent->y - y; - centc++; - } - } - } - } - - if (centc) { - cent->minx = cent->x - xdist / centc * 2; - cent->maxx = cent->x + xdist / centc * 2; - /* Make the box a little bigger in y direction to make sure the - heads fit in so we multiply by 3 instead of 2 which seems to - to work well in practical */ - cent->miny = cent->y - ydist / centc * 3; - cent->maxy = cent->y + ydist / centc * 2; - } - if (cent->maxx > width - 1) - cent->maxx = width - 1; - else if (cent->maxx < 0) - cent->maxx = 0; - if (cent->maxy > height - 1) - cent->maxy = height - 1; - else if (cent->maxy < 0) - cent->maxy = 0; - if (cent->minx > width - 1) - cent->minx = width - 1; - else if (cent->minx < 0) - cent->minx = 0; - if (cent->miny > height - 1) - cent->miny = height - 1; - else if (cent->miny < 0) - cent->miny = 0; - - /* Align for better locate box handling */ - cent->minx += cent->minx % 2; - cent->miny += cent->miny % 2; - cent->maxx -= cent->maxx % 2; - cent->maxy -= cent->maxy % 2; - - cent->width = cent->maxx - cent->minx; - cent->height = cent->maxy - cent->miny; - - /* We want to center Y coordinate to be the center of the action. - The head of a person is important so we correct the cent.y coordinate - to match the correction to include a persons head that we just did above */ - cent->y = (cent->miny + cent->maxy) / 2; - + unsigned char *out = imgs->out; + int *labels = imgs->labels; + int x, y, centc = 0, xdist = 0, ydist = 0; + + cent->x = 0; + cent->y = 0; + cent->maxx = 0; + cent->maxy = 0; + cent->minx = width; + cent->miny = height; + + /* If Labeling enabled - locate center of largest labelgroup */ + if (imgs->labelsize_max) { + /* Locate largest labelgroup */ + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + if (*(labels++) & 32768) { + cent->x += x; + cent->y += y; + centc++; + } + } + } + + } else { + /* Locate movement */ + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + if (*(out++)) { + cent->x += x; + cent->y += y; + centc++; + } + } + } + + } + + if (centc) { + cent->x = cent->x / centc; + cent->y = cent->y / centc; + } + + /* Now we find the size of the Motion */ + + /* First reset pointers back to initial value */ + centc = 0; + labels = imgs->labels; + out = imgs->out; + + /* If Labeling then we find the area around largest labelgroup instead */ + if (imgs->labelsize_max) { + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + if (*(labels++) & 32768) { + if (x > cent->x) + xdist += x - cent->x; + else if (x < cent->x) + xdist += cent->x - x; + if (y > cent->y) + ydist += y - cent->y; + else if (y < cent->y) + ydist += cent->y - y; + centc++; + } + } + } + + } else { + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + if (*(out++)) { + if (x > cent->x) + xdist += x - cent->x; + else if (x < cent->x) + xdist += cent->x - x; + if (y > cent->y) + ydist += y - cent->y; + else if (y < cent->y) + ydist += cent->y - y; + centc++; + } + } + } + + } + + if (centc) { + cent->minx = cent->x - xdist / centc * 2; + cent->maxx = cent->x + xdist / centc * 2; + /* Make the box a little bigger in y direction to make sure the + heads fit in so we multiply by 3 instead of 2 which seems to + to work well in practical */ + cent->miny = cent->y - ydist / centc * 3; + cent->maxy = cent->y + ydist / centc * 2; + } + + if (cent->maxx > width - 1) + cent->maxx = width - 1; + else if (cent->maxx < 0) + cent->maxx = 0; + if (cent->maxy > height - 1) + cent->maxy = height - 1; + else if (cent->maxy < 0) + cent->maxy = 0; + if (cent->minx > width - 1) + cent->minx = width - 1; + else if (cent->minx < 0) + cent->minx = 0; + if (cent->miny > height - 1) + cent->miny = height - 1; + else if (cent->miny < 0) + cent->miny = 0; + + /* Align for better locate box handling */ + cent->minx += cent->minx % 2; + cent->miny += cent->miny % 2; + cent->maxx -= cent->maxx % 2; + cent->maxy -= cent->maxy % 2; + + cent->width = cent->maxx - cent->minx; + cent->height = cent->maxy - cent->miny; + + /* We want to center Y coordinate to be the center of the action. + The head of a person is important so we correct the cent.y coordinate + to match the correction to include a persons head that we just did above */ + cent->y = (cent->miny + cent->maxy) / 2; + } /* draw a box around the movement */ void alg_draw_location(struct coord *cent, struct images *imgs, int width, unsigned char *new, int mode) { - unsigned char *out = imgs->out; - int x, y; - - out = imgs->out; - - /* Draw a box around the movement */ - if (mode == LOCATE_BOTH) { /* both normal and motion image gets a box */ - int width_miny = width * cent->miny; - int width_maxy = width * cent->maxy; - - for (x = cent->minx; x <= cent->maxx; x++) { - int width_miny_x = x + width_miny; - int width_maxy_x = x + width_maxy; - - new[width_miny_x] =~new[width_miny_x]; - new[width_maxy_x] =~new[width_maxy_x]; - out[width_miny_x] =~out[width_miny_x]; - out[width_maxy_x] =~out[width_maxy_x]; - } - for (y = cent->miny; y <= cent->maxy; y++) { - int width_minx_y = cent->minx + y * width; - int width_maxx_y = cent->maxx + y * width; - - new[width_minx_y] =~new[width_minx_y]; - new[width_maxx_y] =~new[width_maxx_y]; - out[width_minx_y] =~out[width_minx_y]; - out[width_maxx_y] =~out[width_maxx_y]; - } - } else if (mode == LOCATE_CENTER) { - int centy = cent->y * width; - - for (x = cent->x - 10; x <= cent->x + 10; x++) { - new[centy + x] =~new[centy + x]; - out[centy + x] =~out[centy + x]; - } - - for (y = cent->y - 10; y <= cent->y + 10; y++) { - new[cent->x + y * width] =~new[cent->x + y * width]; - out[cent->x + y * width] =~out[cent->x + y * width]; - } - - } else { /* normal image only (e.g. preview shot) */ - int width_miny = width * cent->miny; - int width_maxy = width * cent->maxy; - - for (x = cent->minx; x <= cent->maxx; x++) { - int width_miny_x = width_miny + x; - int width_maxy_x = width_maxy + x; - - new[width_miny_x] =~new[width_miny_x]; - new[width_maxy_x] =~new[width_maxy_x]; - } - for (y = cent->miny; y <= cent->maxy; y++) { - int minx_y = cent->minx + y * width; - int maxx_y = cent->maxx + y * width; - - new[minx_y] =~new[minx_y]; - new[maxx_y] =~new[maxx_y]; - } - } + unsigned char *out = imgs->out; + int x, y; + + out = imgs->out; + + /* Draw a box around the movement */ + if (mode == LOCATE_BOTH) { /* both normal and motion image gets a box */ + int width_miny = width * cent->miny; + int width_maxy = width * cent->maxy; + + for (x = cent->minx; x <= cent->maxx; x++) { + int width_miny_x = x + width_miny; + int width_maxy_x = x + width_maxy; + + new[width_miny_x] =~new[width_miny_x]; + new[width_maxy_x] =~new[width_maxy_x]; + out[width_miny_x] =~out[width_miny_x]; + out[width_maxy_x] =~out[width_maxy_x]; + } + + for (y = cent->miny; y <= cent->maxy; y++) { + int width_minx_y = cent->minx + y * width; + int width_maxx_y = cent->maxx + y * width; + + new[width_minx_y] =~new[width_minx_y]; + new[width_maxx_y] =~new[width_maxx_y]; + out[width_minx_y] =~out[width_minx_y]; + out[width_maxx_y] =~out[width_maxx_y]; + } + + } else if (mode == LOCATE_CENTER) { + int centy = cent->y * width; + + for (x = cent->x - 10; x <= cent->x + 10; x++) { + new[centy + x] =~new[centy + x]; + out[centy + x] =~out[centy + x]; + } + + for (y = cent->y - 10; y <= cent->y + 10; y++) { + new[cent->x + y * width] =~new[cent->x + y * width]; + out[cent->x + y * width] =~out[cent->x + y * width]; + } + + } else { /* normal image only (e.g. preview shot) */ + int width_miny = width * cent->miny; + int width_maxy = width * cent->maxy; + + for (x = cent->minx; x <= cent->maxx; x++) { + int width_miny_x = width_miny + x; + int width_maxy_x = width_maxy + x; + + new[width_miny_x] =~new[width_miny_x]; + new[width_maxy_x] =~new[width_maxy_x]; + } + + for (y = cent->miny; y <= cent->maxy; y++) { + int minx_y = cent->minx + y * width; + int maxx_y = cent->maxx + y * width; + + new[minx_y] =~new[minx_y]; + new[maxx_y] =~new[maxx_y]; + } + } } /* draw a RED box around the movement */ void alg_draw_red_location(struct coord *cent, struct images *imgs, int width, unsigned char *new, int mode) { - unsigned char *out = imgs->out; - unsigned char *new_u, *new_v; - int x, y, v, cwidth, cblock; - - cwidth = width / 2; - cblock = imgs->motionsize / 4; - x = imgs->motionsize; - v = x + cblock; - out = imgs->out; - new_u = new + x; - new_v = new + v; - - /* Draw a box around the movement */ - if (mode == LOCATE_BOTH) { /* both normal and motion image gets a box */ - int width_miny = width * cent->miny; - int width_maxy = width * cent->maxy; - int cwidth_miny = cwidth * (cent->miny / 2); - int cwidth_maxy = cwidth * (cent->maxy / 2); - - for (x = cent->minx + 2; x <= cent->maxx - 2; x += 2) { - int width_miny_x = x + width_miny; - int width_maxy_x = x + width_maxy; - int cwidth_miny_x = x / 2 + cwidth_miny; - int cwidth_maxy_x = x / 2 + cwidth_maxy; - - new_u[cwidth_miny_x] = 128; - new_u[cwidth_maxy_x] = 128; - new_v[cwidth_miny_x] = 255; - new_v[cwidth_maxy_x] = 255; - - new[width_miny_x] = 128; - new[width_maxy_x] = 128; - - new[width_miny_x + 1] = 128; - new[width_maxy_x + 1] = 128; - - new[width_miny_x + width] = 128; - new[width_maxy_x + width] = 128; - - new[width_miny_x + 1 + width] = 128; - new[width_maxy_x + 1 + width] = 128; - - out[width_miny_x] =~out[width_miny_x]; - out[width_maxy_x + width] =~out[width_maxy_x + width]; - out[width_miny_x + 1] =~out[width_miny_x + 1]; - out[width_maxy_x + 1 + width] =~out[width_maxy_x + 1 + width]; - } - - for (y = cent->miny; y <= cent->maxy; y += 2) { - int width_minx_y = cent->minx + y * width; - int width_maxx_y = cent->maxx + y * width; - int cwidth_minx_y = (cent->minx / 2) + (y / 2) * cwidth; - int cwidth_maxx_y = (cent->maxx / 2) + (y / 2) * cwidth; - - new_u[cwidth_minx_y] = 128; - new_u[cwidth_maxx_y] = 128; - new_v[cwidth_minx_y] = 255; - new_v[cwidth_maxx_y] = 255; - - new[width_minx_y] = 128; - new[width_maxx_y] = 128; - - new[width_minx_y + width] = 128; - new[width_maxx_y + width] = 128; - - new[width_minx_y + 1] = 128; - new[width_maxx_y + 1] = 128; - - new[width_minx_y + width + 1] = 128; - new[width_maxx_y + width + 1] = 128; - - out[width_minx_y + 1] =~out[width_minx_y + 1]; - out[width_maxx_y] =~out[width_maxx_y]; - out[width_minx_y + width + 1] =~out[width_minx_y + width + 1]; - out[width_maxx_y + width] =~out[width_maxx_y + width]; - } - } else if (mode == LOCATE_CENTER) { - int cwidth_maxy = cwidth * (cent->y / 2); - - for (x = cent->x - 10; x <= cent->x + 10; x += 2) { - int cwidth_maxy_x = x / 2 + cwidth_maxy; - - new_u[cwidth_maxy_x] = 128; - new_v[cwidth_maxy_x] = 255; - } - - for (y = cent->y - 10; y <= cent->y + 10; y += 2) { - int cwidth_minx_y = (cent->x / 2) + (y / 2) * cwidth; - - new_u[cwidth_minx_y] = 128; - new_v[cwidth_minx_y] = 255; - } - - - } else { /* normal image only (e.g. preview shot) */ - int width_miny = width * cent->miny; - int width_maxy = width * cent->maxy; - int cwidth_miny = cwidth * (cent->miny / 2); - int cwidth_maxy = cwidth * (cent->maxy / 2); - - for (x = cent->minx + 2; x <= cent->maxx - 2; x += 2) { - int width_miny_x = x + width_miny; - int width_maxy_x = x + width_maxy; - int cwidth_miny_x = x / 2 + cwidth_miny; - int cwidth_maxy_x = x / 2 + cwidth_maxy; - - new_u[cwidth_miny_x] = 128; - new_u[cwidth_maxy_x] = 128; - new_v[cwidth_miny_x] = 255; - new_v[cwidth_maxy_x] = 255; - - new[width_miny_x] = 128; - new[width_maxy_x] = 128; - - new[width_miny_x + 1] = 128; - new[width_maxy_x + 1] = 128; - - new[width_miny_x + width] = 128; - new[width_maxy_x + width] = 128; - - new[width_miny_x + 1 + width] = 128; - new[width_maxy_x + 1 + width] = 128; - } - - for (y = cent->miny; y <= cent->maxy; y += 2) { - int width_minx_y = cent->minx + y * width; - int width_maxx_y = cent->maxx + y * width; - int cwidth_minx_y = (cent->minx / 2) + (y / 2) * cwidth; - int cwidth_maxx_y = (cent->maxx / 2) + (y / 2) * cwidth; - - new_u[cwidth_minx_y] = 128; - new_u[cwidth_maxx_y] = 128; - new_v[cwidth_minx_y] = 255; - new_v[cwidth_maxx_y] = 255; - - new[width_minx_y] = 128; - new[width_maxx_y] = 128; - - new[width_minx_y + width] = 128; - new[width_maxx_y + width] = 128; - - new[width_minx_y + 1] = 128; - new[width_maxx_y + 1] = 128; - - new[width_minx_y + width + 1] = 128; - new[width_maxx_y + width + 1] = 128; - } - } + unsigned char *out = imgs->out; + unsigned char *new_u, *new_v; + int x, y, v, cwidth, cblock; + + cwidth = width / 2; + cblock = imgs->motionsize / 4; + x = imgs->motionsize; + v = x + cblock; + out = imgs->out; + new_u = new + x; + new_v = new + v; + + /* Draw a box around the movement */ + if (mode == LOCATE_BOTH) { /* both normal and motion image gets a box */ + int width_miny = width * cent->miny; + int width_maxy = width * cent->maxy; + int cwidth_miny = cwidth * (cent->miny / 2); + int cwidth_maxy = cwidth * (cent->maxy / 2); + + for (x = cent->minx + 2; x <= cent->maxx - 2; x += 2) { + int width_miny_x = x + width_miny; + int width_maxy_x = x + width_maxy; + int cwidth_miny_x = x / 2 + cwidth_miny; + int cwidth_maxy_x = x / 2 + cwidth_maxy; + + new_u[cwidth_miny_x] = 128; + new_u[cwidth_maxy_x] = 128; + new_v[cwidth_miny_x] = 255; + new_v[cwidth_maxy_x] = 255; + + new[width_miny_x] = 128; + new[width_maxy_x] = 128; + + new[width_miny_x + 1] = 128; + new[width_maxy_x + 1] = 128; + + new[width_miny_x + width] = 128; + new[width_maxy_x + width] = 128; + + new[width_miny_x + 1 + width] = 128; + new[width_maxy_x + 1 + width] = 128; + + out[width_miny_x] =~out[width_miny_x]; + out[width_maxy_x + width] =~out[width_maxy_x + width]; + out[width_miny_x + 1] =~out[width_miny_x + 1]; + out[width_maxy_x + 1 + width] =~out[width_maxy_x + 1 + width]; + } + + for (y = cent->miny; y <= cent->maxy; y += 2) { + int width_minx_y = cent->minx + y * width; + int width_maxx_y = cent->maxx + y * width; + int cwidth_minx_y = (cent->minx / 2) + (y / 2) * cwidth; + int cwidth_maxx_y = (cent->maxx / 2) + (y / 2) * cwidth; + + new_u[cwidth_minx_y] = 128; + new_u[cwidth_maxx_y] = 128; + new_v[cwidth_minx_y] = 255; + new_v[cwidth_maxx_y] = 255; + + new[width_minx_y] = 128; + new[width_maxx_y] = 128; + + new[width_minx_y + width] = 128; + new[width_maxx_y + width] = 128; + + new[width_minx_y + 1] = 128; + new[width_maxx_y + 1] = 128; + + new[width_minx_y + width + 1] = 128; + new[width_maxx_y + width + 1] = 128; + + out[width_minx_y + 1] =~out[width_minx_y + 1]; + out[width_maxx_y] =~out[width_maxx_y]; + out[width_minx_y + width + 1] =~out[width_minx_y + width + 1]; + out[width_maxx_y + width] =~out[width_maxx_y + width]; + } + + } else if (mode == LOCATE_CENTER) { + int cwidth_maxy = cwidth * (cent->y / 2); + + for (x = cent->x - 10; x <= cent->x + 10; x += 2) { + int cwidth_maxy_x = x / 2 + cwidth_maxy; + + new_u[cwidth_maxy_x] = 128; + new_v[cwidth_maxy_x] = 255; + } + + for (y = cent->y - 10; y <= cent->y + 10; y += 2) { + int cwidth_minx_y = (cent->x / 2) + (y / 2) * cwidth; + + new_u[cwidth_minx_y] = 128; + new_v[cwidth_minx_y] = 255; + } + + + } else { /* normal image only (e.g. preview shot) */ + int width_miny = width * cent->miny; + int width_maxy = width * cent->maxy; + int cwidth_miny = cwidth * (cent->miny / 2); + int cwidth_maxy = cwidth * (cent->maxy / 2); + + for (x = cent->minx + 2; x <= cent->maxx - 2; x += 2) { + int width_miny_x = x + width_miny; + int width_maxy_x = x + width_maxy; + int cwidth_miny_x = x / 2 + cwidth_miny; + int cwidth_maxy_x = x / 2 + cwidth_maxy; + + new_u[cwidth_miny_x] = 128; + new_u[cwidth_maxy_x] = 128; + new_v[cwidth_miny_x] = 255; + new_v[cwidth_maxy_x] = 255; + + new[width_miny_x] = 128; + new[width_maxy_x] = 128; + + new[width_miny_x + 1] = 128; + new[width_maxy_x + 1] = 128; + + new[width_miny_x + width] = 128; + new[width_maxy_x + width] = 128; + + new[width_miny_x + 1 + width] = 128; + new[width_maxy_x + 1 + width] = 128; + } + + for (y = cent->miny; y <= cent->maxy; y += 2) { + int width_minx_y = cent->minx + y * width; + int width_maxx_y = cent->maxx + y * width; + int cwidth_minx_y = (cent->minx / 2) + (y / 2) * cwidth; + int cwidth_maxx_y = (cent->maxx / 2) + (y / 2) * cwidth; + + new_u[cwidth_minx_y] = 128; + new_u[cwidth_maxx_y] = 128; + new_v[cwidth_minx_y] = 255; + new_v[cwidth_maxx_y] = 255; + + new[width_minx_y] = 128; + new[width_maxx_y] = 128; + + new[width_minx_y + width] = 128; + new[width_maxx_y + width] = 128; + + new[width_minx_y + 1] = 128; + new[width_maxx_y + 1] = 128; + + new[width_minx_y + width + 1] = 128; + new[width_maxx_y + width + 1] = 128; + } + } } @@ -374,62 +384,71 @@ void alg_draw_red_location(struct coord *cent, struct images *imgs, int width, u void alg_noise_tune(struct context *cnt, unsigned char *new) { - struct images *imgs = &cnt->imgs; - int i; - unsigned char *ref = imgs->ref; - int diff, sum = 0, count = 0; - unsigned char *mask = imgs->mask; - unsigned char *smartmask = imgs->smartmask_final; - - i = imgs->motionsize; - - for (; i > 0; i--) { - diff = ABS(*ref - *new); - if (mask) - diff = ((diff * *mask++) / 255); - if (*smartmask){ - sum += diff + 1; - count++; - } - ref++; - new++; - smartmask++; - } - if (count > 3) { /* avoid divide by zero */ - sum /= count / 3; - } - cnt->noise = 4 + (cnt->noise + sum) / 2; /* 5: safe, 4: regular, 3: more sensitive */ + struct images *imgs = &cnt->imgs; + int i; + unsigned char *ref = imgs->ref; + int diff, sum = 0, count = 0; + unsigned char *mask = imgs->mask; + unsigned char *smartmask = imgs->smartmask_final; + + i = imgs->motionsize; + + for (; i > 0; i--) { + diff = ABS(*ref - *new); + + if (mask) + diff = ((diff * *mask++) / 255); + + if (*smartmask) { + sum += diff + 1; + count++; + } + + ref++; + new++; + smartmask++; + } + + if (count > 3) /* avoid divide by zero */ + sum /= count / 3; + + cnt->noise = 4 + (cnt->noise + sum) / 2; /* 5: safe, 4: regular, 3: more sensitive */ } void alg_threshold_tune(struct context *cnt, int diffs, int motion) { - int i; - int sum = 0, top = diffs; - - if (!diffs) - return; - - if (motion) - diffs = cnt->threshold / 4; - - for (i = 0; i < THRESHOLD_TUNE_LENGTH - 1; i++) - { - sum += cnt->diffs_last[i]; - if (cnt->diffs_last[i+1] && !motion) - cnt->diffs_last[i] = cnt->diffs_last[i+1]; - else - cnt->diffs_last[i] = cnt->threshold / 4; - if (cnt->diffs_last[i] > top) - top = cnt->diffs_last[i]; - } - sum += cnt->diffs_last[i]; - cnt->diffs_last[i] = diffs; - - sum /= THRESHOLD_TUNE_LENGTH / 4; - if (sum < top * 2) - sum = top * 2; - if (sum < cnt->conf.max_changes) - cnt->threshold = (cnt->threshold + sum) / 2; + int i; + int sum = 0, top = diffs; + + if (!diffs) + return; + + if (motion) + diffs = cnt->threshold / 4; + + for (i = 0; i < THRESHOLD_TUNE_LENGTH - 1; i++) { + + sum += cnt->diffs_last[i]; + + if (cnt->diffs_last[i+1] && !motion) + cnt->diffs_last[i] = cnt->diffs_last[i+1]; + else + cnt->diffs_last[i] = cnt->threshold / 4; + + if (cnt->diffs_last[i] > top) + top = cnt->diffs_last[i]; + } + + sum += cnt->diffs_last[i]; + cnt->diffs_last[i] = diffs; + + sum /= THRESHOLD_TUNE_LENGTH / 4; + + if (sum < top * 2) + sum = top * 2; + + if (sum < cnt->conf.max_changes) + cnt->threshold = (cnt->threshold + sum) / 2; } /* @@ -446,349 +465,367 @@ Floodfill enhanced by Ian McConnel based on code from #define MAXS 10000 /* max depth of stack */ -#define PUSH(Y, XL, XR, DY) /* push new segment on stack */ \ - if (sp= 0 && Y+(DY) < height) \ +#define PUSH(Y, XL, XR, DY) /* push new segment on stack */ \ + if (sp= 0 && Y+(DY) < height) \ {sp->y = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;} -#define POP(Y, XL, XR, DY) /* pop segment off stack */ \ +#define POP(Y, XL, XR, DY) /* pop segment off stack */ \ {sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;} -typedef struct {short y, xl, xr, dy;} Segment; +typedef struct { + short y, xl, xr, dy; +} Segment; -static int iflood(int x, int y, - int width, int height, unsigned char *out, int *labels, int newvalue, int oldvalue) +static int iflood(int x, int y, int width, int height, + unsigned char *out, int *labels, int newvalue, int oldvalue) { - int l, x1, x2, dy; - Segment stack[MAXS], *sp = stack; /* stack of filled segments */ - int count = 0; - - if (x < 0 || x >= width || y < 0 || y >= height) - return 0; - - PUSH(y, x, x, 1); /* needed in some cases */ - PUSH(y+1, x, x, -1); /* seed segment (popped 1st) */ - - while (sp > stack) { - /* pop segment off stack and fill a neighboring scan line */ - POP(y, x1, x2, dy); - /* - * segment of scan line y-dy for x1<=x<=x2 was previously filled, - * now explore adjacent pixels in scan line y - */ - for (x = x1; x >= 0 && out[y * width + x] != 0 && labels[y * width + x] == oldvalue; x--) { - labels[y * width + x] = newvalue; - count++; - } - - if (x >= x1) - goto skip; - - l = x + 1; - - if (l < x1) - PUSH(y, l, x1-1, -dy); /* leak on left? */ - - x = x1 + 1; - - do { - for (; x < width && out[y * width + x] != 0 && labels[y * width + x] == oldvalue; x++) { - labels[y * width + x] = newvalue; - count++; - } - - PUSH(y, l, x-1, dy); - - if (x > x2+1) - PUSH(y, x2+1, x-1, -dy); /* leak on right? */ - - skip: - - for (x++; x <= x2 && !(out[y * width + x] != 0 && labels[y * width + x] == oldvalue); x++); - - l = x; - } while (x <= x2); - } - return count; + int l, x1, x2, dy; + Segment stack[MAXS], *sp = stack; /* stack of filled segments */ + int count = 0; + + if (x < 0 || x >= width || y < 0 || y >= height) + return 0; + + PUSH(y, x, x, 1); /* needed in some cases */ + PUSH(y+1, x, x, -1); /* seed segment (popped 1st) */ + + while (sp > stack) { + /* pop segment off stack and fill a neighboring scan line */ + POP(y, x1, x2, dy); + /* + * segment of scan line y-dy for x1<=x<=x2 was previously filled, + * now explore adjacent pixels in scan line y + */ + for (x = x1; x >= 0 && out[y * width + x] != 0 && labels[y * width + x] == oldvalue; x--) { + labels[y * width + x] = newvalue; + count++; + } + + if (x >= x1) + goto skip; + + l = x + 1; + + if (l < x1) + PUSH(y, l, x1-1, -dy); /* leak on left? */ + + x = x1 + 1; + + do { + for (; x < width && out[y * width + x] != 0 && labels[y * width + x] == oldvalue; x++) { + labels[y * width + x] = newvalue; + count++; + } + + PUSH(y, l, x-1, dy); + + if (x > x2+1) + PUSH(y, x2+1, x-1, -dy); /* leak on right? */ + + skip: + + for (x++; x <= x2 && !(out[y * width + x] != 0 && labels[y * width + x] == oldvalue); x++); + + l = x; + } while (x <= x2); + } + return count; } static int alg_labeling(struct context *cnt) { - struct images *imgs = &cnt->imgs; - unsigned char *out = imgs->out; - int *labels = imgs->labels; - int ix, iy, pixelpos; - int width = imgs->width; - int height = imgs->height; - int labelsize = 0; - int current_label = 2; - - cnt->current_image->total_labels = 0; - imgs->labelsize_max = 0; - /* ALL labels above threshold are counted as labelgroup */ - imgs->labelgroup_max = 0; - imgs->labels_above = 0; - - /* init: 0 means no label set / not checked */ - memset(labels, 0, width * height * sizeof(labels)); - pixelpos = 0; - for (iy = 0; iy < height - 1; iy++) { - for (ix = 0; ix < width - 1; ix++, pixelpos++) { - /* no motion - no label */ - if( out[pixelpos] == 0 ) { - labels[pixelpos] = 1; - continue; - } - - /* already visited by iflood */ - if (labels[pixelpos] > 0) - continue; - labelsize = iflood(ix, iy, width, height, out, labels, current_label, 0); - - if (labelsize > 0) { - //printf( "Label: %i (%i) Size: %i (%i,%i)\n", current_label, cnt->current_image->total_labels, labelsize, ix, iy ); - /* Label above threshold? Mark it again (add 32768 to labelnumber) */ - if (labelsize > cnt->threshold) { - labelsize = iflood(ix, iy, width, height, out, labels, current_label + 32768, current_label); - imgs->labelgroup_max += labelsize; - imgs->labels_above++; - } - - if (imgs->labelsize_max < labelsize) { - imgs->labelsize_max = labelsize; - imgs->largest_label = current_label; - } - - cnt->current_image->total_labels++; - current_label++; - } - } - pixelpos++; /* compensate for ixtotal_labels, imgs->labelsize_max, cnt->current_image->largest_label); - /* return group of significant labels */ - return imgs->labelgroup_max; + struct images *imgs = &cnt->imgs; + unsigned char *out = imgs->out; + int *labels = imgs->labels; + int ix, iy, pixelpos; + int width = imgs->width; + int height = imgs->height; + int labelsize = 0; + int current_label = 2; + + cnt->current_image->total_labels = 0; + imgs->labelsize_max = 0; + /* ALL labels above threshold are counted as labelgroup */ + imgs->labelgroup_max = 0; + imgs->labels_above = 0; + + /* init: 0 means no label set / not checked */ + memset(labels, 0, width * height * sizeof(labels)); + pixelpos = 0; + + for (iy = 0; iy < height - 1; iy++) { + for (ix = 0; ix < width - 1; ix++, pixelpos++) { + /* no motion - no label */ + if (out[pixelpos] == 0) { + labels[pixelpos] = 1; + continue; + } + + /* already visited by iflood */ + if (labels[pixelpos] > 0) + continue; + + labelsize = iflood(ix, iy, width, height, out, labels, current_label, 0); + + if (labelsize > 0) { + if (debug_level >= CAMERA_VERBOSE) + motion_log(LOG_DEBUG, 0,"%s: Label: %i (%i) Size: %i (%i,%i)\n", + __FUNCTION__, current_label, cnt->current_image->total_labels, + labelsize, ix, iy); + + /* Label above threshold? Mark it again (add 32768 to labelnumber) */ + if (labelsize > cnt->threshold) { + labelsize = iflood(ix, iy, width, height, out, labels, current_label + 32768, current_label); + imgs->labelgroup_max += labelsize; + imgs->labels_above++; + } + + if (imgs->labelsize_max < labelsize) { + imgs->labelsize_max = labelsize; + imgs->largest_label = current_label; + } + + cnt->current_image->total_labels++; + current_label++; + } + } + pixelpos++; /* compensate for ix= CAMERA_VERBOSE) + motion_log(LOG_DEBUG, 0,"%s: %i Labels found. Largest connected Area: %i Pixel(s). " + "Largest Label: %i\n", __FUNCTION__, imgs->largest_label, imgs->labelsize_max, + cnt->current_image->total_labels); + + /* return group of significant labels */ + return imgs->labelgroup_max; } /* Dilates a 3x3 box */ static int dilate9(unsigned char *img, int width, int height, void *buffer) { - /* - row1, row2 and row3 represent lines in the temporary buffer - * - window is a sliding window containing max values of the columns - * in the 3x3 matrix - * - widx is an index into the sliding window (this is faster than - * doing modulo 3 on i) - * - blob keeps the current max value - */ - int y, i, sum = 0, widx; - unsigned char *row1, *row2, *row3, *rowTemp,*yp; - unsigned char window[3], blob, latest; - - /* Set up row pointers in the temporary buffer. */ - row1 = buffer; - row2 = row1 + width; - row3 = row2 + width; - - /* Init rows 2 and 3. */ - memset(row2, 0, width); - memcpy(row3, img, width); - - /* Pointer to the current row in img. */ - yp = img; - - for (y = 0; y < height; y++) { - /* Move down one step; row 1 becomes the previous row 2 and so on. */ - rowTemp = row1; - row1 = row2; - row2 = row3; - row3 = rowTemp; - - /* If we're at the last row, fill with zeros, otherwise copy from img. */ - if(y == height - 1) - memset(row3, 0, width); - else - memcpy(row3, yp+width, width); - - /* Init slots 0 and 1 in the moving window. */ - window[0] = MAX3(row1[0], row2[0], row3[0]); - window[1] = MAX3(row1[1], row2[1], row3[1]); - - /* Init blob to the current max, and set window index. */ - blob = MAX2(window[0], window[1]); - widx = 2; - - /* Iterate over the current row; index i is off by one to eliminate - * a lot of +1es in the loop. - */ - for (i = 2; i <= width - 1; i++) { - /* Get the max value of the next column in the 3x3 matrix. */ - latest = window[widx] = MAX3(row1[i], row2[i], row3[i]); - - /* If the value is larger than the current max, use it. Otherwise, - * calculate a new max (because the new value may not be the max. - */ - if(latest >= blob) - blob = latest; - else - blob = MAX3(window[0], window[1], window[2]); - - /* Write the max value (blob) to the image. */ - if (blob != 0) { - *(yp + i - 1) = blob; - sum++; - } - - /* Wrap around the window index if necessary. */ - if(++widx == 3) - widx = 0; - } - - /* Store zeros in the vertical sides. */ - *yp = *(yp + width - 1) = 0; - yp += width; - } - - return sum; + /* - row1, row2 and row3 represent lines in the temporary buffer + * - window is a sliding window containing max values of the columns + * in the 3x3 matrix + * - widx is an index into the sliding window (this is faster than + * doing modulo 3 on i) + * - blob keeps the current max value + */ + int y, i, sum = 0, widx; + unsigned char *row1, *row2, *row3, *rowTemp,*yp; + unsigned char window[3], blob, latest; + + /* Set up row pointers in the temporary buffer. */ + row1 = buffer; + row2 = row1 + width; + row3 = row2 + width; + + /* Init rows 2 and 3. */ + memset(row2, 0, width); + memcpy(row3, img, width); + + /* Pointer to the current row in img. */ + yp = img; + + for (y = 0; y < height; y++) { + /* Move down one step; row 1 becomes the previous row 2 and so on. */ + rowTemp = row1; + row1 = row2; + row2 = row3; + row3 = rowTemp; + + /* If we're at the last row, fill with zeros, otherwise copy from img. */ + if (y == height - 1) + memset(row3, 0, width); + else + memcpy(row3, yp+width, width); + + /* Init slots 0 and 1 in the moving window. */ + window[0] = MAX3(row1[0], row2[0], row3[0]); + window[1] = MAX3(row1[1], row2[1], row3[1]); + + /* Init blob to the current max, and set window index. */ + blob = MAX2(window[0], window[1]); + widx = 2; + + /* Iterate over the current row; index i is off by one to eliminate + * a lot of +1es in the loop. + */ + for (i = 2; i <= width - 1; i++) { + /* Get the max value of the next column in the 3x3 matrix. */ + latest = window[widx] = MAX3(row1[i], row2[i], row3[i]); + + /* If the value is larger than the current max, use it. Otherwise, + * calculate a new max (because the new value may not be the max. + */ + if (latest >= blob) + blob = latest; + else + blob = MAX3(window[0], window[1], window[2]); + + /* Write the max value (blob) to the image. */ + if (blob != 0) { + *(yp + i - 1) = blob; + sum++; + } + + /* Wrap around the window index if necessary. */ + if (++widx == 3) + widx = 0; + } + + /* Store zeros in the vertical sides. */ + *yp = *(yp + width - 1) = 0; + yp += width; + } + + return sum; } /* Dilates a + shape */ static int dilate5(unsigned char *img, int width, int height, void *buffer) { - /* - row1, row2 and row3 represent lines in the temporary buffer - * - mem holds the max value of the overlapping part of two + shapes - */ - int y, i, sum = 0; - unsigned char *row1, *row2, *row3, *rowTemp, *yp; - unsigned char blob, mem, latest; - - /* Set up row pointers in the temporary buffer. */ - row1 = buffer; - row2 = row1 + width; - row3 = row2 + width; - - /* Init rows 2 and 3. */ - memset(row2, 0, width); - memcpy(row3, img, width); - - /* Pointer to the current row in img. */ - yp = img; - - for (y = 0; y < height; y++) { - /* Move down one step; row 1 becomes the previous row 2 and so on. */ - rowTemp = row1; - row1 = row2; - row2 = row3; - row3 = rowTemp; - - /* If we're at the last row, fill with zeros, otherwise copy from img. */ - if (y == height - 1) - memset(row3, 0, width); - else - memcpy(row3, yp + width, width); - - /* Init mem and set blob to force an evaluation of the entire + shape. */ - mem = MAX2(row2[0], row2[1]); - blob = 1; /* dummy value, must be > 0 */ - - for (i = 1; i < width - 1; i++) { - /* Get the max value of the "right edge" of the + shape. */ - latest = MAX3(row1[i], row2[i + 1], row3[i]); - - if (blob == 0) { - /* In case the last blob is zero, only latest matters. */ - blob = latest; - mem = row2[i + 1]; - } else { - /* Otherwise, we have to check both latest and mem. */ - blob = MAX2(mem, latest); - mem = MAX2(row2[i], row2[i + 1]); - } - - /* Write the max value (blob) to the image. */ - if (blob != 0) { - *(yp + i) = blob; - sum++; - } - } - - /* Store zeros in the vertical sides. */ - *yp = *(yp + width - 1) = 0; - yp += width; - } - return sum; + /* - row1, row2 and row3 represent lines in the temporary buffer + * - mem holds the max value of the overlapping part of two + shapes + */ + int y, i, sum = 0; + unsigned char *row1, *row2, *row3, *rowTemp, *yp; + unsigned char blob, mem, latest; + + /* Set up row pointers in the temporary buffer. */ + row1 = buffer; + row2 = row1 + width; + row3 = row2 + width; + + /* Init rows 2 and 3. */ + memset(row2, 0, width); + memcpy(row3, img, width); + + /* Pointer to the current row in img. */ + yp = img; + + for (y = 0; y < height; y++) { + /* Move down one step; row 1 becomes the previous row 2 and so on. */ + rowTemp = row1; + row1 = row2; + row2 = row3; + row3 = rowTemp; + + /* If we're at the last row, fill with zeros, otherwise copy from img. */ + if (y == height - 1) + memset(row3, 0, width); + else + memcpy(row3, yp + width, width); + + /* Init mem and set blob to force an evaluation of the entire + shape. */ + mem = MAX2(row2[0], row2[1]); + blob = 1; /* dummy value, must be > 0 */ + + for (i = 1; i < width - 1; i++) { + /* Get the max value of the "right edge" of the + shape. */ + latest = MAX3(row1[i], row2[i + 1], row3[i]); + + if (blob == 0) { + /* In case the last blob is zero, only latest matters. */ + blob = latest; + mem = row2[i + 1]; + } else { + /* Otherwise, we have to check both latest and mem. */ + blob = MAX2(mem, latest); + mem = MAX2(row2[i], row2[i + 1]); + } + + /* Write the max value (blob) to the image. */ + if (blob != 0) { + *(yp + i) = blob; + sum++; + } + } + + /* Store zeros in the vertical sides. */ + *yp = *(yp + width - 1) = 0; + yp += width; + } + return sum; } /* Erodes a 3x3 box */ static int erode9(unsigned char *img, int width, int height, void *buffer, unsigned char flag) { - int y, i, sum = 0; - char *Row1,*Row2,*Row3; - - Row1 = buffer; - Row2 = Row1 + width; - Row3 = Row1 + 2 * width; - memset(Row2, flag, width); - memcpy(Row3, img, width); - for (y = 0; y < height; y++) { - memcpy(Row1, Row2, width); - memcpy(Row2, Row3, width); - if (y == height-1) - memset(Row3, flag, width); - else - memcpy(Row3, img + (y+1) * width, width); - - for (i = width - 2; i >= 1; i--) { - if (Row1[i - 1] == 0 || - Row1[i] == 0 || - Row1[i + 1] == 0 || - Row2[i - 1] == 0 || - Row2[i] == 0 || - Row2[i + 1] == 0 || - Row3[i - 1] == 0 || - Row3[i] == 0 || - Row3[i + 1] == 0) - img[y * width + i] = 0; - else - sum++; - } - img[y * width] = img[y * width + width - 1] = flag; - } - return sum; + int y, i, sum = 0; + char *Row1,*Row2,*Row3; + + Row1 = buffer; + Row2 = Row1 + width; + Row3 = Row1 + 2 * width; + memset(Row2, flag, width); + memcpy(Row3, img, width); + + for (y = 0; y < height; y++) { + memcpy(Row1, Row2, width); + memcpy(Row2, Row3, width); + + if (y == height-1) + memset(Row3, flag, width); + else + memcpy(Row3, img + (y+1) * width, width); + + for (i = width - 2; i >= 1; i--) { + if (Row1[i - 1] == 0 || + Row1[i] == 0 || + Row1[i + 1] == 0 || + Row2[i - 1] == 0 || + Row2[i] == 0 || + Row2[i + 1] == 0 || + Row3[i - 1] == 0 || + Row3[i] == 0 || + Row3[i + 1] == 0) + img[y * width + i] = 0; + else + sum++; + } + + img[y * width] = img[y * width + width - 1] = flag; + } + return sum; } /* Erodes in a + shape */ static int erode5(unsigned char *img, int width, int height, void *buffer, unsigned char flag) { - int y, i, sum = 0; - char *Row1,*Row2,*Row3; - - Row1 = buffer; - Row2 = Row1 + width; - Row3 = Row1 + 2 * width; - memset(Row2, flag, width); - memcpy(Row3, img, width); - - for (y = 0; y < height; y++) { - memcpy(Row1, Row2, width); - memcpy(Row2, Row3, width); - if (y == height-1) - memset(Row3, flag, width); - else - memcpy(Row3, img + (y + 1) * width, width); - - for (i = width - 2; i >= 1; i--) { - if (Row1[i] == 0 || - Row2[i - 1] == 0 || - Row2[i] == 0 || - Row2[i + 1] == 0 || - Row3[i] == 0) - img[y * width + i] = 0; - else - sum++; - } - img[y * width] = img[y * width + width - 1] = flag; - } - return sum; + int y, i, sum = 0; + char *Row1,*Row2,*Row3; + + Row1 = buffer; + Row2 = Row1 + width; + Row3 = Row1 + 2 * width; + memset(Row2, flag, width); + memcpy(Row3, img, width); + + for (y = 0; y < height; y++) { + memcpy(Row1, Row2, width); + memcpy(Row2, Row3, width); + + if (y == height-1) + memset(Row3, flag, width); + else + memcpy(Row3, img + (y + 1) * width, width); + + for (i = width - 2; i >= 1; i--) { + if (Row1[i] == 0 || + Row2[i - 1] == 0 || + Row2[i] == 0 || + Row2[i + 1] == 0 || + Row3[i] == 0) + img[y * width + i] = 0; + else + sum++; + } + + img[y * width] = img[y * width + width - 1] = flag; + } + return sum; } /* @@ -796,83 +833,87 @@ static int erode5(unsigned char *img, int width, int height, void *buffer, unsig */ int alg_despeckle(struct context *cnt, int olddiffs) { - int diffs = 0; - unsigned char *out = cnt->imgs.out; - int width = cnt->imgs.width; - int height = cnt->imgs.height; - int done = 0, i, len = strlen(cnt->conf.despeckle_filter); - unsigned char *common_buffer = cnt->imgs.common_buffer; - - for (i = 0; i < len; i++) { - switch (cnt->conf.despeckle_filter[i]) { - case 'E': - if ((diffs = erode9(out, width, height, common_buffer, 0)) == 0) i = len; - done = 1; - break; - case 'e': - if ((diffs = erode5(out, width, height, common_buffer, 0)) == 0) i = len; - done = 1; - break; - case 'D': - diffs = dilate9(out, width, height, common_buffer); - done = 1; - break; - case 'd': - diffs = dilate5(out, width, height, common_buffer); - done = 1; - break; - /* no further despeckle after labeling! */ - case 'l': - diffs = alg_labeling(cnt); - i = len; - done = 2; - break; - } - } - - /* If conf.despeckle_filter contains any valid action EeDdl */ - if (done){ - if (done != 2) cnt->imgs.labelsize_max = 0; // Disable Labeling - return diffs; - } - else - cnt->imgs.labelsize_max = 0; // Disable Labeling - - return olddiffs; + int diffs = 0; + unsigned char *out = cnt->imgs.out; + int width = cnt->imgs.width; + int height = cnt->imgs.height; + int done = 0, i, len = strlen(cnt->conf.despeckle_filter); + unsigned char *common_buffer = cnt->imgs.common_buffer; + + for (i = 0; i < len; i++) { + switch (cnt->conf.despeckle_filter[i]) { + case 'E': + if ((diffs = erode9(out, width, height, common_buffer, 0)) == 0) + i = len; + done = 1; + break; + case 'e': + if ((diffs = erode5(out, width, height, common_buffer, 0)) == 0) + i = len; + done = 1; + break; + case 'D': + diffs = dilate9(out, width, height, common_buffer); + done = 1; + break; + case 'd': + diffs = dilate5(out, width, height, common_buffer); + done = 1; + break; + /* no further despeckle after labeling! */ + case 'l': + diffs = alg_labeling(cnt); + i = len; + done = 2; + break; + } + } + + /* If conf.despeckle_filter contains any valid action EeDdl */ + if (done) { + if (done != 2) + cnt->imgs.labelsize_max = 0; // Disable Labeling + return diffs; + } else { + cnt->imgs.labelsize_max = 0; // Disable Labeling + } + + return olddiffs; } /* Generate actual smartmask. Calculate sensitivity based on motion */ void alg_tune_smartmask(struct context *cnt) { - int i, diff; - int motionsize = cnt->imgs.motionsize; - unsigned char *smartmask = cnt->imgs.smartmask; - unsigned char *smartmask_final = cnt->imgs.smartmask_final; - int *smartmask_buffer = cnt->imgs.smartmask_buffer; - int sensitivity = cnt->lastrate * (11 - cnt->smartmask_speed); - - for (i = 0; i < motionsize; i++) { - /* Decrease smart_mask sensitivity every 5*speed seconds only */ - if (smartmask[i] > 0) - smartmask[i]--; - /* Increase smart_mask sensitivity based on the buffered values */ - diff = smartmask_buffer[i]/sensitivity; - if (diff){ - if (smartmask[i] <= diff + 80) - smartmask[i] += diff; - else - smartmask[i] = 80; - smartmask_buffer[i] %= sensitivity; - } - /* Transfer raw mask to the final stage when above trigger value */ - if (smartmask[i] > 20) - smartmask_final[i] = 0; - else - smartmask_final[i] = 255; - } - /* Further expansion (here:erode due to inverted logic!) of the mask */ - diff = erode9(smartmask_final, cnt->imgs.width, cnt->imgs.height, cnt->imgs.common_buffer, 255); - diff = erode5(smartmask_final, cnt->imgs.width, cnt->imgs.height, cnt->imgs.common_buffer, 255); + int i, diff; + int motionsize = cnt->imgs.motionsize; + unsigned char *smartmask = cnt->imgs.smartmask; + unsigned char *smartmask_final = cnt->imgs.smartmask_final; + int *smartmask_buffer = cnt->imgs.smartmask_buffer; + int sensitivity = cnt->lastrate * (11 - cnt->smartmask_speed); + + for (i = 0; i < motionsize; i++) { + /* Decrease smart_mask sensitivity every 5*speed seconds only */ + if (smartmask[i] > 0) + smartmask[i]--; + /* Increase smart_mask sensitivity based on the buffered values */ + diff = smartmask_buffer[i]/sensitivity; + + if (diff) { + if (smartmask[i] <= diff + 80) + smartmask[i] += diff; + else + smartmask[i] = 80; + smartmask_buffer[i] %= sensitivity; + } + /* Transfer raw mask to the final stage when above trigger value */ + if (smartmask[i] > 20) + smartmask_final[i] = 0; + else + smartmask_final[i] = 255; + } + /* Further expansion (here:erode due to inverted logic!) of the mask */ + diff = erode9(smartmask_final, cnt->imgs.width, cnt->imgs.height, cnt->imgs.common_buffer, 255); + diff = erode5(smartmask_final, cnt->imgs.width, cnt->imgs.height, cnt->imgs.common_buffer, 255); } /* Increment for *smartmask_buffer in alg_diff_standard. */ @@ -880,284 +921,285 @@ void alg_tune_smartmask(struct context *cnt) int alg_diff_standard (struct context *cnt, unsigned char *new) { - struct images *imgs = &cnt->imgs; - int i, diffs = 0; - int noise = cnt->noise; - int smartmask_speed = cnt->smartmask_speed; - unsigned char *ref = imgs->ref; - unsigned char *out = imgs->out; - unsigned char *mask = imgs->mask; - unsigned char *smartmask_final = imgs->smartmask_final; - int *smartmask_buffer = imgs->smartmask_buffer; + struct images *imgs = &cnt->imgs; + int i, diffs = 0; + int noise = cnt->noise; + int smartmask_speed = cnt->smartmask_speed; + unsigned char *ref = imgs->ref; + unsigned char *out = imgs->out; + unsigned char *mask = imgs->mask; + unsigned char *smartmask_final = imgs->smartmask_final; + int *smartmask_buffer = imgs->smartmask_buffer; #ifdef HAVE_MMX - mmx_t mmtemp; /* used for transferring to/from memory */ - int unload; /* counter for unloading diff counts */ + mmx_t mmtemp; /* used for transferring to/from memory */ + int unload; /* counter for unloading diff counts */ #endif - i = imgs->motionsize; - memset(out+i, 128, i/2); /* motion pictures are now b/w i.o. green */ - /* Keeping this memset in the MMX case when zeroes are necessarily - * written anyway seems to be beneficial in terms of speed. Perhaps a - * cache thing? - */ - memset(out, 0, i); + i = imgs->motionsize; + memset(out+i, 128, i/2); /* motion pictures are now b/w i.o. green */ + /* Keeping this memset in the MMX case when zeroes are necessarily + * written anyway seems to be beneficial in terms of speed. Perhaps a + * cache thing? + */ + memset(out, 0, i); #ifdef HAVE_MMX - /* NOTE: The Pentium has two instruction pipes: U and V. I have grouped MMX - * instructions in pairs according to how I think they will be scheduled in - * the U and V pipes. Due to pairing constraints, the V pipe will sometimes - * be empty (for example, memory access always goes into the U pipe). - * - * The following MMX registers are kept throughout the loop: - * mm5 - 8 separate diff counters (unloaded periodically) - * mm6 - mask: 00ff 00ff 00ff 00ff - * mm7 - noise level as 8 packed bytes - * - * -- Per Jonsson - */ - - /* To avoid a div, we work with differences multiplied by 255 in the - * default case and *mask otherwise. Thus, the limit to compare with is - * 255*(noise+1)-1). - */ - mmtemp.uw[0] = mmtemp.uw[1] = mmtemp.uw[2] = mmtemp.uw[3] = - (unsigned short)(noise * 255 + 254); - - /* Reset mm5 to zero, set the mm6 mask, and store the multiplied noise - * level as four words in mm7. - */ - movq_m2r(mmtemp, mm7); /* U */ - pcmpeqb_r2r(mm6, mm6); /* V */ - - pxor_r2r(mm5, mm5); /* U */ - psrlw_i2r(8, mm6); /* V */ - - /* We must unload mm5 every 255th round, because the diffs accumulate - * in each packed byte, which can hold at most 255 diffs before it - * gets saturated. - */ - unload = 255; - - for (; i > 7; i -= 8) { - /* Calculate abs(*ref-*new) for 8 pixels in parallel. */ - movq_m2r(*ref, mm0); /* U: mm0 = r7 r6 r5 r4 r3 r2 r1 r0 */ - pxor_r2r(mm4, mm4); /* V: mm4 = 0 */ - - movq_m2r(*new, mm1); /* U: mm1 = n7 n6 n5 n4 n3 n2 n1 n0 */ - movq_r2r(mm0, mm2); /* V: mm2 = r7 r6 r5 r4 r3 r2 r1 r0 */ - - /* These subtractions are saturated, i.e. won't go below 0. */ - psubusb_r2r(mm1, mm0); /* U: mm0 = (r7-n7) ... (r0-n0) */ - psubusb_r2r(mm2, mm1); /* V: mm1 = (n7-r7) ... (n0-r0) */ - - /* Each byte dX in mm0 is abs(nX-rX). */ - por_r2r(mm1, mm0); /* U: mm0 = d7 d6 d5 d4 d3 d2 d1 d0 */ - - /* Expand the absolute differences to words in mm0 and mm1. */ - movq_r2r(mm0, mm1); /* U: mm1 = d7 d6 d5 d4 d3 d2 d1 d0 */ - punpcklbw_r2r(mm4, mm0); /* V: mm0 = d3 d2 d1 d0 */ - - punpckhbw_r2r(mm4, mm1); /* U: mm1 = d7 d6 d5 d4 */ - - if (mask) { - /* Load and expand 8 mask bytes to words in mm2 and mm3. Then - * multiply by mm0 and mm1, respectively. - */ - movq_m2r(*mask, mm2); /* U: mm2 = m7 m6 m5 m4 m3 m2 m1 m0 */ - - movq_r2r(mm2, mm3); /* U: mm3 = m7 m6 m5 m4 m3 m2 m1 m0 */ - punpcklbw_r2r(mm4, mm2); /* v: mm2 = m3 m2 m1 m0 */ - - punpckhbw_r2r(mm4, mm3); /* U: mm3 = m7 m6 m5 m4 */ - pmullw_r2r(mm2, mm0); /* V: mm0 = (d3*m3) ... (d0*m0) */ - - pmullw_r2r(mm3, mm1); /* U: mm1 = (d7*m7) ... (d4*m4) */ - - mask += 8; - } else { - /* Not using mask - multiply the absolute differences by 255. We - * do this by left-shifting 8 places and then subtracting dX. - */ - movq_r2r(mm0, mm2); /* U: mm2 = d3 d2 d1 d0 */ - psllw_i2r(8, mm0); /* V: mm2 = (256*d3) ... (256*d0) */ - - movq_r2r(mm1, mm3); /* U: mm3 = d7 d6 d5 d4 */ - psllw_i2r(8, mm1); /* V: mm3 = (256*d7) ... (256*d4) */ - - psubusw_r2r(mm2, mm0); /* U */ - psubusw_r2r(mm3, mm1); /* V */ - } - - /* Next, compare the multiplied absolute differences with the multiplied - * noise level (repeated as 4 words in mm7), resulting in a "motion flag" - * for each pixel. - * - * Since pcmpgtw performs signed comparisons, we have to subtract noise, - * test for equality to 0 and then invert the result. - * - * Note that it is safe to generate the "motion flags" before the - * smartmask code, as all that can happen is that individual flags get - * reset to 0 because of the smartmask. - */ - psubusw_r2r(mm7, mm0); /* U: subtract by (multiplied) noise */ - psubusw_r2r(mm7, mm1); /* V */ - - pcmpeqw_r2r(mm4, mm0); /* U: test for equality with 0 */ - pcmpeqw_r2r(mm4, mm1); /* V */ - - pand_r2r(mm6, mm0); /* U: convert 0xffff -> 0x00ff */ - pand_r2r(mm6, mm1); /* V */ - - pxor_r2r(mm6, mm0); /* U: invert the result */ - pxor_r2r(mm6, mm1); /* V */ - - /* Each fX is the "motion flag" = 0 for no motion, 0xff for motion. */ - packuswb_r2r(mm1, mm0); /* U: mm0 = f7 f6 f5 f4 f3 f2 f1 f0 */ - - if (smartmask_speed) { - /* Apply the smartmask. Basically, if *smartmask_final is 0, the - * corresponding "motion flag" in mm0 will be reset. - */ - movq_m2r(*smartmask_final, mm3); /* U: mm3 = s7 s6 s5 s4 s3 s2 s1 s0 */ - - /* ...but move the "motion flags" to memory before, in order to - * increment *smartmask_buffer properly below. - */ - movq_r2m(mm0, mmtemp); /* U */ - pcmpeqb_r2r(mm4, mm3); /* V: mm3 = 0xff where sX==0 */ - - /* ANDN negates the target before anding. */ - pandn_r2r(mm0, mm3); /* U: mm3 = 0xff where dX>noise && sX>0 */ - - movq_r2r(mm3, mm0); /* U */ - - /* Add to *smartmask_buffer. This is probably the fastest way to do it. */ - if (cnt->event_nr != cnt->prev_event) { - if (mmtemp.ub[0]) smartmask_buffer[0] += SMARTMASK_SENSITIVITY_INCR; - if (mmtemp.ub[1]) smartmask_buffer[1] += SMARTMASK_SENSITIVITY_INCR; - if (mmtemp.ub[2]) smartmask_buffer[2] += SMARTMASK_SENSITIVITY_INCR; - if (mmtemp.ub[3]) smartmask_buffer[3] += SMARTMASK_SENSITIVITY_INCR; - if (mmtemp.ub[4]) smartmask_buffer[4] += SMARTMASK_SENSITIVITY_INCR; - if (mmtemp.ub[5]) smartmask_buffer[5] += SMARTMASK_SENSITIVITY_INCR; - if (mmtemp.ub[6]) smartmask_buffer[6] += SMARTMASK_SENSITIVITY_INCR; - if (mmtemp.ub[7]) smartmask_buffer[7] += SMARTMASK_SENSITIVITY_INCR; - } - - smartmask_buffer += 8; - smartmask_final += 8; - } - - movq_m2r(*new, mm2); /* U: mm1 = n7 n6 n5 n4 n3 n2 n1 n0 */ - - /* Cancel out pixels in *new according to the "motion flags" in mm0. - * Each NX is either 0 or nX as from *new. - */ - pand_r2r(mm0, mm2); /* U: mm1 = N7 N6 N5 N4 N3 N2 N1 N0 */ - psubb_r2r(mm0, mm4); /* V: mm4 = 0x01 where dX>noise */ - - /* mm5 holds 8 separate counts - each one is increased according to - * the contents of mm4 (where each byte is either 0x00 or 0x01). - */ - movq_r2m(mm2, *out); /* U: this will stall */ - paddusb_r2r(mm4, mm5); /* V: add counts to mm5 */ - - /* Every 255th turn, we need to unload mm5 into the diffs variable, - * because otherwise the packed bytes will get saturated. - */ - if (--unload == 0) { - /* Unload mm5 to memory and reset it. */ - movq_r2m(mm5, mmtemp); /* U */ - pxor_r2r(mm5, mm5); /* V: mm5 = 0 */ - - diffs += mmtemp.ub[0] + mmtemp.ub[1] + mmtemp.ub[2] + mmtemp.ub[3] + - mmtemp.ub[4] + mmtemp.ub[5] + mmtemp.ub[6] + mmtemp.ub[7]; - unload = 255; - } - - out += 8; - ref += 8; - new += 8; - } - - /* Check if there are diffs left in mm5 that need to be copied to the - * diffs variable. - */ - if (unload < 255) { - movq_r2m(mm5, mmtemp); - diffs += mmtemp.ub[0] + mmtemp.ub[1] + mmtemp.ub[2] + mmtemp.ub[3] + - mmtemp.ub[4] + mmtemp.ub[5] + mmtemp.ub[6] + mmtemp.ub[7]; - } - - emms(); + /* NOTE: The Pentium has two instruction pipes: U and V. I have grouped MMX + * instructions in pairs according to how I think they will be scheduled in + * the U and V pipes. Due to pairing constraints, the V pipe will sometimes + * be empty (for example, memory access always goes into the U pipe). + * + * The following MMX registers are kept throughout the loop: + * mm5 - 8 separate diff counters (unloaded periodically) + * mm6 - mask: 00ff 00ff 00ff 00ff + * mm7 - noise level as 8 packed bytes + * + * -- Per Jonsson + */ + + /* To avoid a div, we work with differences multiplied by 255 in the + * default case and *mask otherwise. Thus, the limit to compare with is + * 255*(noise+1)-1). + */ + mmtemp.uw[0] = mmtemp.uw[1] = mmtemp.uw[2] = mmtemp.uw[3] = + (unsigned short)(noise * 255 + 254); + + /* Reset mm5 to zero, set the mm6 mask, and store the multiplied noise + * level as four words in mm7. + */ + movq_m2r(mmtemp, mm7); /* U */ + pcmpeqb_r2r(mm6, mm6); /* V */ + + pxor_r2r(mm5, mm5); /* U */ + psrlw_i2r(8, mm6); /* V */ + + /* We must unload mm5 every 255th round, because the diffs accumulate + * in each packed byte, which can hold at most 255 diffs before it + * gets saturated. + */ + unload = 255; + + for (; i > 7; i -= 8) { + /* Calculate abs(*ref-*new) for 8 pixels in parallel. */ + movq_m2r(*ref, mm0); /* U: mm0 = r7 r6 r5 r4 r3 r2 r1 r0 */ + pxor_r2r(mm4, mm4); /* V: mm4 = 0 */ + + movq_m2r(*new, mm1); /* U: mm1 = n7 n6 n5 n4 n3 n2 n1 n0 */ + movq_r2r(mm0, mm2); /* V: mm2 = r7 r6 r5 r4 r3 r2 r1 r0 */ + + /* These subtractions are saturated, i.e. won't go below 0. */ + psubusb_r2r(mm1, mm0); /* U: mm0 = (r7-n7) ... (r0-n0) */ + psubusb_r2r(mm2, mm1); /* V: mm1 = (n7-r7) ... (n0-r0) */ + + /* Each byte dX in mm0 is abs(nX-rX). */ + por_r2r(mm1, mm0); /* U: mm0 = d7 d6 d5 d4 d3 d2 d1 d0 */ + + /* Expand the absolute differences to words in mm0 and mm1. */ + movq_r2r(mm0, mm1); /* U: mm1 = d7 d6 d5 d4 d3 d2 d1 d0 */ + punpcklbw_r2r(mm4, mm0); /* V: mm0 = d3 d2 d1 d0 */ + + punpckhbw_r2r(mm4, mm1); /* U: mm1 = d7 d6 d5 d4 */ + + if (mask) { + /* Load and expand 8 mask bytes to words in mm2 and mm3. Then + * multiply by mm0 and mm1, respectively. + */ + movq_m2r(*mask, mm2); /* U: mm2 = m7 m6 m5 m4 m3 m2 m1 m0 */ + + movq_r2r(mm2, mm3); /* U: mm3 = m7 m6 m5 m4 m3 m2 m1 m0 */ + punpcklbw_r2r(mm4, mm2); /* v: mm2 = m3 m2 m1 m0 */ + + punpckhbw_r2r(mm4, mm3); /* U: mm3 = m7 m6 m5 m4 */ + pmullw_r2r(mm2, mm0); /* V: mm0 = (d3*m3) ... (d0*m0) */ + + pmullw_r2r(mm3, mm1); /* U: mm1 = (d7*m7) ... (d4*m4) */ + + mask += 8; + } else { + /* Not using mask - multiply the absolute differences by 255. We + * do this by left-shifting 8 places and then subtracting dX. + */ + movq_r2r(mm0, mm2); /* U: mm2 = d3 d2 d1 d0 */ + psllw_i2r(8, mm0); /* V: mm2 = (256*d3) ... (256*d0) */ + + movq_r2r(mm1, mm3); /* U: mm3 = d7 d6 d5 d4 */ + psllw_i2r(8, mm1); /* V: mm3 = (256*d7) ... (256*d4) */ + + psubusw_r2r(mm2, mm0); /* U */ + psubusw_r2r(mm3, mm1); /* V */ + } + + /* Next, compare the multiplied absolute differences with the multiplied + * noise level (repeated as 4 words in mm7), resulting in a "motion flag" + * for each pixel. + * + * Since pcmpgtw performs signed comparisons, we have to subtract noise, + * test for equality to 0 and then invert the result. + * + * Note that it is safe to generate the "motion flags" before the + * smartmask code, as all that can happen is that individual flags get + * reset to 0 because of the smartmask. + */ + psubusw_r2r(mm7, mm0); /* U: subtract by (multiplied) noise */ + psubusw_r2r(mm7, mm1); /* V */ + + pcmpeqw_r2r(mm4, mm0); /* U: test for equality with 0 */ + pcmpeqw_r2r(mm4, mm1); /* V */ + + pand_r2r(mm6, mm0); /* U: convert 0xffff -> 0x00ff */ + pand_r2r(mm6, mm1); /* V */ + + pxor_r2r(mm6, mm0); /* U: invert the result */ + pxor_r2r(mm6, mm1); /* V */ + + /* Each fX is the "motion flag" = 0 for no motion, 0xff for motion. */ + packuswb_r2r(mm1, mm0); /* U: mm0 = f7 f6 f5 f4 f3 f2 f1 f0 */ + + if (smartmask_speed) { + /* Apply the smartmask. Basically, if *smartmask_final is 0, the + * corresponding "motion flag" in mm0 will be reset. + */ + movq_m2r(*smartmask_final, mm3); /* U: mm3 = s7 s6 s5 s4 s3 s2 s1 s0 */ + + /* ...but move the "motion flags" to memory before, in order to + * increment *smartmask_buffer properly below. + */ + movq_r2m(mm0, mmtemp); /* U */ + pcmpeqb_r2r(mm4, mm3); /* V: mm3 = 0xff where sX==0 */ + + /* ANDN negates the target before anding. */ + pandn_r2r(mm0, mm3); /* U: mm3 = 0xff where dX>noise && sX>0 */ + + movq_r2r(mm3, mm0); /* U */ + + /* Add to *smartmask_buffer. This is probably the fastest way to do it. */ + if (cnt->event_nr != cnt->prev_event) { + if (mmtemp.ub[0]) smartmask_buffer[0] += SMARTMASK_SENSITIVITY_INCR; + if (mmtemp.ub[1]) smartmask_buffer[1] += SMARTMASK_SENSITIVITY_INCR; + if (mmtemp.ub[2]) smartmask_buffer[2] += SMARTMASK_SENSITIVITY_INCR; + if (mmtemp.ub[3]) smartmask_buffer[3] += SMARTMASK_SENSITIVITY_INCR; + if (mmtemp.ub[4]) smartmask_buffer[4] += SMARTMASK_SENSITIVITY_INCR; + if (mmtemp.ub[5]) smartmask_buffer[5] += SMARTMASK_SENSITIVITY_INCR; + if (mmtemp.ub[6]) smartmask_buffer[6] += SMARTMASK_SENSITIVITY_INCR; + if (mmtemp.ub[7]) smartmask_buffer[7] += SMARTMASK_SENSITIVITY_INCR; + } + + smartmask_buffer += 8; + smartmask_final += 8; + } + + movq_m2r(*new, mm2); /* U: mm1 = n7 n6 n5 n4 n3 n2 n1 n0 */ + + /* Cancel out pixels in *new according to the "motion flags" in mm0. + * Each NX is either 0 or nX as from *new. + */ + pand_r2r(mm0, mm2); /* U: mm1 = N7 N6 N5 N4 N3 N2 N1 N0 */ + psubb_r2r(mm0, mm4); /* V: mm4 = 0x01 where dX>noise */ + + /* mm5 holds 8 separate counts - each one is increased according to + * the contents of mm4 (where each byte is either 0x00 or 0x01). + */ + movq_r2m(mm2, *out); /* U: this will stall */ + paddusb_r2r(mm4, mm5); /* V: add counts to mm5 */ + + /* Every 255th turn, we need to unload mm5 into the diffs variable, + * because otherwise the packed bytes will get saturated. + */ + if (--unload == 0) { + /* Unload mm5 to memory and reset it. */ + movq_r2m(mm5, mmtemp); /* U */ + pxor_r2r(mm5, mm5); /* V: mm5 = 0 */ + + diffs += mmtemp.ub[0] + mmtemp.ub[1] + mmtemp.ub[2] + mmtemp.ub[3] + + mmtemp.ub[4] + mmtemp.ub[5] + mmtemp.ub[6] + mmtemp.ub[7]; + unload = 255; + } + + out += 8; + ref += 8; + new += 8; + } + + /* Check if there are diffs left in mm5 that need to be copied to the + * diffs variable. + */ + if (unload < 255) { + movq_r2m(mm5, mmtemp); + diffs += mmtemp.ub[0] + mmtemp.ub[1] + mmtemp.ub[2] + mmtemp.ub[3] + + mmtemp.ub[4] + mmtemp.ub[5] + mmtemp.ub[6] + mmtemp.ub[7]; + } + + emms(); #endif - /* Note that the non-MMX code is present even if the MMX code is present. - * This is necessary if the resolution is not a multiple of 8, in which - * case the non-MMX code needs to take care of the remaining pixels. - */ - - for (; i > 0; i--) { - register unsigned char curdiff = (int)(abs(*ref - *new)); /* using a temp variable is 12% faster */ - /* apply fixed mask */ - if (mask) - curdiff = ((int)(curdiff * *mask++) / 255); - - if (smartmask_speed) { - if (curdiff > noise) { - /* increase smart_mask sensitivity every frame when motion - is detected. (with speed=5, mask is increased by 1 every - second. To be able to increase by 5 every second (with - speed=10) we add 5 here. NOT related to the 5 at ratio- - calculation. */ - if (cnt->event_nr != cnt->prev_event) - (*smartmask_buffer) += SMARTMASK_SENSITIVITY_INCR; - /* apply smart_mask */ - if (!*smartmask_final) - curdiff = 0; - } - smartmask_final++; - smartmask_buffer++; - } - /* Pixel still in motion after all the masks? */ - if (curdiff > noise) { - *out = *new; - diffs++; - } - out++; - ref++; - new++; - } - return diffs; + /* Note that the non-MMX code is present even if the MMX code is present. + * This is necessary if the resolution is not a multiple of 8, in which + * case the non-MMX code needs to take care of the remaining pixels. + */ + + for (; i > 0; i--) { + register unsigned char curdiff = (int)(abs(*ref - *new)); /* using a temp variable is 12% faster */ + /* apply fixed mask */ + if (mask) + curdiff = ((int)(curdiff * *mask++) / 255); + + if (smartmask_speed) { + if (curdiff > noise) { + /* increase smart_mask sensitivity every frame when motion + is detected. (with speed=5, mask is increased by 1 every + second. To be able to increase by 5 every second (with + speed=10) we add 5 here. NOT related to the 5 at ratio- + calculation. */ + if (cnt->event_nr != cnt->prev_event) + (*smartmask_buffer) += SMARTMASK_SENSITIVITY_INCR; + /* apply smart_mask */ + if (!*smartmask_final) + curdiff = 0; + } + smartmask_final++; + smartmask_buffer++; + } + /* Pixel still in motion after all the masks? */ + if (curdiff > noise) { + *out = *new; + diffs++; + } + out++; + ref++; + new++; + } + return diffs; } /* - Very fast diff function, does not apply mask overlaying. + Very fast diff function, does not apply mask overlaying. */ static char alg_diff_fast(struct context *cnt, int max_n_changes, unsigned char *new) { - struct images *imgs = &cnt->imgs; - int i, diffs = 0, step = imgs->motionsize/10000; - int noise = cnt->noise; - unsigned char *ref = imgs->ref; - - if (!step % 2) - step++; - /* we're checking only 1 of several pixels */ - max_n_changes /= step; - - i = imgs->motionsize; - for (; i > 0; i -= step) { - register unsigned char curdiff = (int)(abs((char)(*ref - *new))); /* using a temp variable is 12% faster */ - if (curdiff > noise) { - diffs++; - if (diffs > max_n_changes) - return 1; - } - ref += step; - new += step; - } - - return 0; + struct images *imgs = &cnt->imgs; + int i, diffs = 0, step = imgs->motionsize/10000; + int noise = cnt->noise; + unsigned char *ref = imgs->ref; + + if (!step % 2) + step++; + /* we're checking only 1 of several pixels */ + max_n_changes /= step; + + i = imgs->motionsize; + + for (; i > 0; i -= step) { + register unsigned char curdiff = (int)(abs((char)(*ref - *new))); /* using a temp variable is 12% faster */ + if (curdiff > noise) { + diffs++; + if (diffs > max_n_changes) + return 1; + } + ref += step; + new += step; + } + + return 0; } /* alg_diff uses diff_fast to quickly decide if there is anything worth @@ -1165,12 +1207,12 @@ static char alg_diff_fast(struct context *cnt, int max_n_changes, unsigned char */ int alg_diff(struct context *cnt, unsigned char *new) { - int diffs = 0; - - if (alg_diff_fast(cnt, cnt->conf.max_changes / 2, new)) - diffs = alg_diff_standard(cnt, new); + int diffs = 0; + + if (alg_diff_fast(cnt, cnt->conf.max_changes / 2, new)) + diffs = alg_diff_standard(cnt, new); - return diffs; + return diffs; } /* Detect a sudden massive change in the picture. @@ -1179,52 +1221,51 @@ int alg_diff(struct context *cnt, unsigned char *new) */ int alg_lightswitch(struct context *cnt, int diffs) { - struct images *imgs = &cnt->imgs; - - if (cnt->conf.lightswitch < 0) - cnt->conf.lightswitch = 0; - if (cnt->conf.lightswitch > 100) - cnt->conf.lightswitch = 100; - - /* is lightswitch percent of the image changed? */ - if (diffs > (imgs->motionsize * cnt->conf.lightswitch / 100)) - return 1; - - return 0; + struct images *imgs = &cnt->imgs; + + if (cnt->conf.lightswitch < 0) + cnt->conf.lightswitch = 0; + if (cnt->conf.lightswitch > 100) + cnt->conf.lightswitch = 100; + + /* is lightswitch percent of the image changed? */ + if (diffs > (imgs->motionsize * cnt->conf.lightswitch / 100)) + return 1; + + return 0; } int alg_switchfilter(struct context *cnt, int diffs, unsigned char *newimg) { - int linediff = diffs / cnt->imgs.height; - unsigned char *out = cnt->imgs.out; - int y, x, line; - int lines = 0, vertlines = 0; - - for (y = 0; y < cnt->imgs.height; y++) { - line = 0; - for (x = 0; x < cnt->imgs.width; x++) { - if (*(out++)) { - line++; - } - } - if (line > cnt->imgs.width / 18) { - vertlines++; - } - if (line > linediff * 2) { - lines++; - } - } - - if (vertlines > cnt->imgs.height / 10 && lines < vertlines / 3 && - (vertlines > cnt->imgs.height / 4 || lines - vertlines > lines / 2)) { - if (cnt->conf.text_changes) { - char tmp[80]; - sprintf(tmp, "%d %d", lines, vertlines); - draw_text(newimg, cnt->imgs.width - 10, 20, cnt->imgs.width, tmp, cnt->conf.text_double); - } - return diffs; - } - return 0; + int linediff = diffs / cnt->imgs.height; + unsigned char *out = cnt->imgs.out; + int y, x, line; + int lines = 0, vertlines = 0; + + for (y = 0; y < cnt->imgs.height; y++) { + line = 0; + for (x = 0; x < cnt->imgs.width; x++) { + if (*(out++)) + line++; + } + + if (line > cnt->imgs.width / 18) + vertlines++; + + if (line > linediff * 2) + lines++; + } + + if (vertlines > cnt->imgs.height / 10 && lines < vertlines / 3 && + (vertlines > cnt->imgs.height / 4 || lines - vertlines > lines / 2)) { + if (cnt->conf.text_changes) { + char tmp[80]; + sprintf(tmp, "%d %d", lines, vertlines); + draw_text(newimg, cnt->imgs.width - 10, 20, cnt->imgs.width, tmp, cnt->conf.text_double); + } + return diffs; + } + return 0; } /** @@ -1241,52 +1282,53 @@ int alg_switchfilter(struct context *cnt, int diffs, unsigned char *newimg) * */ /* Seconds */ -#define ACCEPT_STATIC_OBJECT_TIME 10 +#define ACCEPT_STATIC_OBJECT_TIME 0 #define EXCLUDE_LEVEL_PERCENT 20 void alg_update_reference_frame(struct context *cnt, int action) { - int accept_timer = cnt->lastrate * ACCEPT_STATIC_OBJECT_TIME; - int i, threshold_ref; - int *ref_dyn = cnt->imgs.ref_dyn; - unsigned char *image_virgin = cnt->imgs.image_virgin; - unsigned char *ref = cnt->imgs.ref; - unsigned char *smartmask = cnt->imgs.smartmask_final; - unsigned char *out = cnt->imgs.out; - - if (cnt->lastrate > 5) /* match rate limit */ - accept_timer /= (cnt->lastrate / 3); - - if (action == UPDATE_REF_FRAME) { /* black&white only for better performance */ - threshold_ref = cnt->noise * EXCLUDE_LEVEL_PERCENT / 100; - for (i = cnt->imgs.motionsize; i > 0; i--) { - /* exclude pixels from ref frame well below noise level */ - if (((int)(abs(*ref - *image_virgin)) > threshold_ref) && (*smartmask)) { - if (*ref_dyn == 0) { /* Always give new pixels a chance */ - *ref_dyn = 1; - } - else if (*ref_dyn > accept_timer) { /* Include static Object after some time */ - *ref_dyn = 0; - *ref = *image_virgin; - } - else if (*out) - (*ref_dyn)++; /* Motionpixel? Keep excluding from ref frame */ - else { - *ref_dyn = 0; /* Nothing special - release pixel */ - *ref = (*ref + *image_virgin) / 2; - } - } - else { /* No motion: copy to ref frame */ - *ref_dyn = 0; /* reset pixel */ - *ref = *image_virgin; - } - ref++; - image_virgin++; - smartmask++; - ref_dyn++; - out++; - } /* end for i */ - } else { /* action == RESET_REF_FRAME - also used to initialize the frame at startup */ - memcpy(cnt->imgs.ref, cnt->imgs.image_virgin, cnt->imgs.size); /* copy fresh image */ - memset(cnt->imgs.ref_dyn, 0, cnt->imgs.motionsize * sizeof(cnt->imgs.ref_dyn)); /* reset static objects */ - } + int accept_timer = cnt->lastrate * ACCEPT_STATIC_OBJECT_TIME; + int i, threshold_ref; + int *ref_dyn = cnt->imgs.ref_dyn; + unsigned char *image_virgin = cnt->imgs.image_virgin; + unsigned char *ref = cnt->imgs.ref; + unsigned char *smartmask = cnt->imgs.smartmask_final; + unsigned char *out = cnt->imgs.out; + + if (cnt->lastrate > 5) /* match rate limit */ + accept_timer /= (cnt->lastrate / 3); + + if (action == UPDATE_REF_FRAME) { /* black&white only for better performance */ + threshold_ref = cnt->noise * EXCLUDE_LEVEL_PERCENT / 100; + + for (i = cnt->imgs.motionsize; i > 0; i--) { + /* exclude pixels from ref frame well below noise level */ + if (((int)(abs(*ref - *image_virgin)) > threshold_ref) && (*smartmask)) { + if (*ref_dyn == 0) { /* Always give new pixels a chance */ + *ref_dyn = 1; + } else if (*ref_dyn > accept_timer) { /* Include static Object after some time */ + *ref_dyn = 0; + *ref = *image_virgin; + } else if (*out) { + (*ref_dyn)++; /* Motionpixel? Keep excluding from ref frame */ + } else { + *ref_dyn = 0; /* Nothing special - release pixel */ + *ref = (*ref + *image_virgin) / 2; + } + + } else { /* No motion: copy to ref frame */ + *ref_dyn = 0; /* reset pixel */ + *ref = *image_virgin; + } + + ref++; + image_virgin++; + smartmask++; + ref_dyn++; + out++; + } /* end for i */ + + } else { /* action == RESET_REF_FRAME - also used to initialize the frame at startup */ + memcpy(cnt->imgs.ref, cnt->imgs.image_virgin, cnt->imgs.size); /* copy fresh image */ + memset(cnt->imgs.ref_dyn, 0, cnt->imgs.motionsize * sizeof(cnt->imgs.ref_dyn)); /* reset static objects */ + } } diff --git a/alg.h b/alg.h index 3b98058..9dafeb5 100644 --- a/alg.h +++ b/alg.h @@ -1,9 +1,9 @@ -/* alg.h +/* alg.h * - * Detect changes in a video stream. - * Copyright 2001 by Jeroen Vreeken (pe1rxq@amsat.org) - * This software is distributed under the GNU public license version 2 - * See also the file 'COPYING'. + * Detect changes in a video stream. + * Copyright 2001 by Jeroen Vreeken (pe1rxq@amsat.org) + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. * */ @@ -13,22 +13,22 @@ #include "motion.h" struct coord { - int x; - int y; - int width; - int height; - int minx; - int maxx; - int miny; - int maxy; + int x; + int y; + int width; + int height; + int minx; + int maxx; + int miny; + int maxy; }; struct segment { - struct coord coord; - int width; - int height; - int open; - int count; + struct coord coord; + int width; + int height; + int open; + int count; }; void alg_locate_center_size(struct images *, int width, int height, struct coord *); diff --git a/conf.c b/conf.c index cc75393..48e10fb 100644 --- a/conf.c +++ b/conf.c @@ -35,9 +35,9 @@ #ifndef HAVE_GET_CURRENT_DIR_NAME char *get_current_dir_name(void) { - char *buf = mymalloc(MAXPATHLEN); - getwd(buf); - return buf; + char *buf = mymalloc(MAXPATHLEN); + getwd(buf); + return buf; } #endif @@ -46,106 +46,106 @@ char *get_current_dir_name(void) struct config conf_template = { - width: DEF_WIDTH, - height: DEF_HEIGHT, - quality: DEF_QUALITY, - rotate_deg: 0, - max_changes: DEF_CHANGES, - threshold_tune: 0, - output_pictures: "on", - motion_img: 0, - emulate_motion: 0, - event_gap: DEF_EVENT_GAP, - max_movie_time: DEF_MAXMOVIETIME, - snapshot_interval: 0, - locate_motion: "off", - input: IN_DEFAULT, - norm: 0, - frame_limit: DEF_MAXFRAMERATE, - quiet: 1, - picture_type: "jpeg", - noise: DEF_NOISELEVEL, - noise_tune: 1, - minimum_frame_time: 0, - lightswitch: 0, - autobright: 0, - brightness: 0, - contrast: 0, - saturation: 0, - hue: 0, - roundrobin_frames: 1, - roundrobin_skip: 1, - pre_capture: 0, - post_capture: 0, - switchfilter: 0, - ffmpeg_output: 0, - ffmpeg_output_debug: 0, - ffmpeg_bps: DEF_FFMPEG_BPS, - ffmpeg_vbr: DEF_FFMPEG_VBR, - ffmpeg_video_codec: DEF_FFMPEG_CODEC, - stream_port: 0, - stream_quality: 50, - stream_motion: 0, - stream_maxrate: 1, - stream_localhost: 1, - stream_limit: 0, - webcontrol_port: 0, - webcontrol_localhost: 1, - webcontrol_html_output: 1, - webcontrol_authentication: NULL, - frequency: 0, - tuner_number: 0, - timelapse: 0, - timelapse_mode: DEF_TIMELAPSE_MODE, + width: DEF_WIDTH, + height: DEF_HEIGHT, + quality: DEF_QUALITY, + rotate_deg: 0, + max_changes: DEF_CHANGES, + threshold_tune: 0, + output_pictures: "on", + motion_img: 0, + emulate_motion: 0, + event_gap: DEF_EVENT_GAP, + max_movie_time: DEF_MAXMOVIETIME, + snapshot_interval: 0, + locate_motion: "off", + input: IN_DEFAULT, + norm: 0, + frame_limit: DEF_MAXFRAMERATE, + quiet: 1, + picture_type: "jpeg", + noise: DEF_NOISELEVEL, + noise_tune: 1, + minimum_frame_time: 0, + lightswitch: 0, + autobright: 0, + brightness: 0, + contrast: 0, + saturation: 0, + hue: 0, + roundrobin_frames: 1, + roundrobin_skip: 1, + pre_capture: 0, + post_capture: 0, + switchfilter: 0, + ffmpeg_output: 0, + ffmpeg_output_debug: 0, + ffmpeg_bps: DEF_FFMPEG_BPS, + ffmpeg_vbr: DEF_FFMPEG_VBR, + ffmpeg_video_codec: DEF_FFMPEG_CODEC, + stream_port: 0, + stream_quality: 50, + stream_motion: 0, + stream_maxrate: 1, + stream_localhost: 1, + stream_limit: 0, + webcontrol_port: 0, + webcontrol_localhost: 1, + webcontrol_html_output: 1, + webcontrol_authentication: NULL, + frequency: 0, + tuner_number: 0, + timelapse: 0, + timelapse_mode: DEF_TIMELAPSE_MODE, #if (defined(BSD)) - tuner_device: NULL, + tuner_device: NULL, #endif - video_device: VIDEO_DEVICE, - v4l2_palette: 8, - vidpipe: NULL, - filepath: NULL, - imagepath: DEF_IMAGEPATH, - moviepath: DEF_MOVIEPATH, - snappath: DEF_SNAPPATH, - timepath: DEF_TIMEPATH, - on_event_start: NULL, - on_event_end: NULL, - mask_file: NULL, - smart_mask_speed: 0, -#if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) - sql_log_image: 1, - sql_log_snapshot: 1, - sql_log_movie: 0, - sql_log_timelapse: 0, - sql_query: DEF_SQL_QUERY, - database_type: NULL, - database_dbname: NULL, - database_host: "localhost", - database_user: NULL, - database_password: NULL, - database_port: 0, -#endif - on_picture_save: NULL, - on_motion_detected: NULL, - on_area_detected: NULL, - on_movie_start: NULL, - on_movie_end: NULL, - on_camera_lost: NULL, - motionvidpipe: NULL, - netcam_url: NULL, - netcam_userpass: NULL, - netcam_keepalive: "off", /* Choices: off, force or on */ - netcam_proxy: NULL, - netcam_broken: 0, - text_changes: 0, - text_left: NULL, - text_right: DEF_TIMESTAMP, - text_event: DEF_EVENTSTAMP, - text_double: 0, - despeckle_filter: NULL, - area_detect: NULL, - minimum_motion_frames: 1, - pid_file: NULL, + video_device: VIDEO_DEVICE, + v4l2_palette: 8, + vidpipe: NULL, + filepath: NULL, + imagepath: DEF_IMAGEPATH, + moviepath: DEF_MOVIEPATH, + snappath: DEF_SNAPPATH, + timepath: DEF_TIMEPATH, + on_event_start: NULL, + on_event_end: NULL, + mask_file: NULL, + smart_mask_speed: 0, +#if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) + sql_log_image: 1, + sql_log_snapshot: 1, + sql_log_movie: 0, + sql_log_timelapse: 0, + sql_query: DEF_SQL_QUERY, + database_type: NULL, + database_dbname: NULL, + database_host: "localhost", + database_user: NULL, + database_password: NULL, + database_port: 0, +#endif + on_picture_save: NULL, + on_motion_detected: NULL, + on_area_detected: NULL, + on_movie_start: NULL, + on_movie_end: NULL, + on_camera_lost: NULL, + motionvidpipe: NULL, + netcam_url: NULL, + netcam_userpass: NULL, + netcam_keepalive: "off", + netcam_proxy: NULL, + netcam_broken: 0, + text_changes: 0, + text_left: NULL, + text_right: DEF_TIMESTAMP, + text_event: DEF_EVENTSTAMP, + text_double: 0, + despeckle_filter: NULL, + area_detect: NULL, + minimum_motion_frames: 1, + pid_file: NULL, }; @@ -165,1214 +165,1214 @@ static void usage(void); /* Pointer magic to determine relative addresses of variables to a struct context pointer */ -#define CNT_OFFSET(varname) ( (int)&((struct context *)NULL)->varname ) -#define CONF_OFFSET(varname) ( (int)&((struct context *)NULL)->conf.varname ) -#define TRACK_OFFSET(varname) ( (int)&((struct context *)NULL)->track.varname ) +#define CNT_OFFSET(varname) ((int)&((struct context *)NULL)->varname) +#define CONF_OFFSET(varname) ((int)&((struct context *)NULL)->conf.varname) +#define TRACK_OFFSET(varname) ((int)&((struct context *)NULL)->track.varname) config_param config_params[] = { - { - "daemon", - "############################################################\n" - "# Daemon\n" - "############################################################\n\n" - "# Start in daemon (background) mode and release terminal (default: off)", - 1, - CNT_OFFSET(daemon), - copy_bool, - print_bool - }, - { - "process_id_file", - "#File to store the process ID, also called pid file. (default: not defined)", - 1, - CONF_OFFSET(pid_file), - copy_string, - print_string - }, - { - "setup_mode", - "############################################################\n" - "# Basic Setup Mode\n" - "############################################################\n\n" - "# Start in Setup-Mode, daemon disabled. (default: off)", - 0, - CONF_OFFSET(setup_mode), - copy_bool, - print_bool - }, - { - "videodevice", - "\n###########################################################\n" - "# Capture device options\n" - "############################################################\n\n" - "# Videodevice to be used for capturing (default /dev/video0)\n" - "# for FreeBSD default is /dev/bktr0", - 0, - CONF_OFFSET(video_device), - copy_string, - print_string - }, - { - "v4l2_palette", - "# v4l2_palette allow to choose preferable palette to be use by motion\n" - "# to capture from those supported by your videodevice. ( default: 8)\n" - "# i.ex if your videodevice supports V4L2_PIX_FMT_SBGGR8 and\n" - "# V4L2_PIX_FMT_MJPEG by default motion will use V4L2_PIX_FMT_MJPEG so\n" - "# set v4l2_palette 1 to force motion use V4L2_PIX_FMT_SBGGR8 instead.\n" - "#\n" - "# Values :\n" - "#\n" - "# V4L2_PIX_FMT_SN9C10X : 0 'S910'\n" - "# V4L2_PIX_FMT_SBGGR8 : 1 'BA81'\n" - "# V4L2_PIX_FMT_MJPEG : 2 'MJPEG'\n" - "# V4L2_PIX_FMT_JPEG : 3 'JPEG'\n" - "# V4L2_PIX_FMT_RGB24 : 4 'RGB3'\n" - "# V4L2_PIX_FMT_UYVY : 5 'UYVY'\n" - "# V4L2_PIX_FMT_YUYV : 6 'YUYV'\n" - "# V4L2_PIX_FMT_YUV422P : 7 '422P'\n" - "# V4L2_PIX_FMT_YUV420 : 8 'YU12'", - 0, - CONF_OFFSET(v4l2_palette), - copy_short, - print_short - }, + { + "daemon", + "############################################################\n" + "# Daemon\n" + "############################################################\n\n" + "# Start in daemon (background) mode and release terminal (default: off)", + 1, + CNT_OFFSET(daemon), + copy_bool, + print_bool + }, + { + "process_id_file", + "#File to store the process ID, also called pid file. (default: not defined)", + 1, + CONF_OFFSET(pid_file), + copy_string, + print_string + }, + { + "setup_mode", + "############################################################\n" + "# Basic Setup Mode\n" + "############################################################\n\n" + "# Start in Setup-Mode, daemon disabled. (default: off)", + 0, + CONF_OFFSET(setup_mode), + copy_bool, + print_bool + }, + { + "videodevice", + "\n###########################################################\n" + "# Capture device options\n" + "############################################################\n\n" + "# Videodevice to be used for capturing (default /dev/video0)\n" + "# for FreeBSD default is /dev/bktr0", + 0, + CONF_OFFSET(video_device), + copy_string, + print_string + }, + { + "v4l2_palette", + "# v4l2_palette allow to choose preferable palette to be use by motion\n" + "# to capture from those supported by your videodevice. ( default: 8)\n" + "# i.ex if your videodevice supports V4L2_PIX_FMT_SBGGR8 and\n" + "# V4L2_PIX_FMT_MJPEG by default motion will use V4L2_PIX_FMT_MJPEG so\n" + "# set v4l2_palette 1 to force motion use V4L2_PIX_FMT_SBGGR8 instead.\n" + "#\n" + "# Values :\n" + "#\n" + "# V4L2_PIX_FMT_SN9C10X : 0 'S910'\n" + "# V4L2_PIX_FMT_SBGGR8 : 1 'BA81'\n" + "# V4L2_PIX_FMT_MJPEG : 2 'MJPEG'\n" + "# V4L2_PIX_FMT_JPEG : 3 'JPEG'\n" + "# V4L2_PIX_FMT_RGB24 : 4 'RGB3'\n" + "# V4L2_PIX_FMT_UYVY : 5 'UYVY'\n" + "# V4L2_PIX_FMT_YUYV : 6 'YUYV'\n" + "# V4L2_PIX_FMT_YUV422P : 7 '422P'\n" + "# V4L2_PIX_FMT_YUV420 : 8 'YU12'", + 0, + CONF_OFFSET(v4l2_palette), + copy_short, + print_short + }, #if (defined(BSD)) - { - "tunerdevice", - "# Tuner device to be used for capturing using tuner as source (default /dev/tuner0)\n" - "# This is ONLY used for FreeBSD. Leave it commented out for Linux", - 0, - CONF_OFFSET(tuner_device), - copy_string, - print_string - }, + { + "tunerdevice", + "# Tuner device to be used for capturing using tuner as source (default /dev/tuner0)\n" + "# This is ONLY used for FreeBSD. Leave it commented out for Linux", + 0, + CONF_OFFSET(tuner_device), + copy_string, + print_string + }, #endif - { - "input", - "# The video input to be used (default: 8)\n" - "# Should normally be set to 0 or 1 for video/TV cards, and 8 for USB cameras", - 0, - CONF_OFFSET(input), - copy_int, - print_int - }, - { - "norm", - "# The video norm to use (only for video capture and TV tuner cards)\n" - "# Values: 0 (PAL), 1 (NTSC), 2 (SECAM), 3 (PAL NC no colour). Default: 0 (PAL)", - 0, - CONF_OFFSET(norm), - copy_int, - print_int - }, - { - "frequency", - "# The frequency to set the tuner to (kHz) (only for TV tuner cards) (default: 0)", - 0, - CONF_OFFSET(frequency), - copy_int, - print_int - }, - { - "rotate", - "# Rotate image this number of degrees. The rotation affects all saved images as\n" - "# well as movies. Valid values: 0 (default = no rotation), 90, 180 and 270.", - 0, - CONF_OFFSET(rotate_deg), - copy_int, - print_int - }, - { - "width", - "# Image width (pixels). Valid range: Camera dependent, default: 352", - 0, - CONF_OFFSET(width), - copy_int, - print_int - }, - { - "height", - "# Image height (pixels). Valid range: Camera dependent, default: 288", - 0, - CONF_OFFSET(height), - copy_int, - print_int - }, - { - "framerate", - "# Maximum number of frames to be captured per second.\n" - "# Valid range: 2-100. Default: 100 (almost no limit).", - 0, - CONF_OFFSET(frame_limit), - copy_int, - print_int - }, - { - "minimum_frame_time", - "# Minimum time in seconds between capturing picture frames from the camera.\n" - "# Default: 0 = disabled - the capture rate is given by the camera framerate.\n" - "# This option is used when you want to capture images at a rate lower than 2 per second.", - 0, - CONF_OFFSET(minimum_frame_time), - copy_int, - print_int - }, - { - "netcam_url", - "# URL to use if you are using a network camera, size will be autodetected (incl http:// ftp:// or file:///)\n" - "# Must be a URL that returns single jpeg pictures or a raw mjpeg stream. Default: Not defined", - 0, - CONF_OFFSET(netcam_url), - copy_string, - print_string - }, - { - "netcam_userpass", - "# Username and password for network camera (only if required). Default: not defined\n" - "# Syntax is user:password", - 0, - CONF_OFFSET(netcam_userpass), - copy_string, - print_string - }, - { - "netcam_keepalive", - "# The setting for keep-alive of network socket, should improve performance on compatible net cameras.\n" - "# off : the historical implementation using HTTP/1.0, closing the socket after each http request.\n" - "# force : Use HTTP/1.0 requests with keep alive header to reuse the same connection.\n" - "# on : Use HTTP/1.1 requests that support keep alive as default.\n" - "# Default: off", - 0, - CONF_OFFSET(netcam_keepalive), - copy_string, - print_string - }, - { - "netcam_proxy", - "# URL to use for a netcam proxy server, if required, e.g. \"http://myproxy\".\n" - "# If a port number other than 80 is needed, use \"http://myproxy:1234\".\n" - "# Default: not defined", - 0, - CONF_OFFSET(netcam_proxy), - copy_string, - print_string - }, - { - "netcam_broken", - "# Set less strict jpeg checks for network cameras with a poor/buggy firmware.\n" - "# Default: off", - 0, - CONF_OFFSET(netcam_broken), - copy_bool, - print_bool - }, - { - "auto_brightness", - "# Let motion regulate the brightness of a video device (default: off).\n" - "# The auto_brightness feature uses the brightness option as its target value.\n" - "# If brightness is zero auto_brightness will adjust to average brightness value 128.\n" - "# Only recommended for cameras without auto brightness", - 0, - CONF_OFFSET(autobright), - copy_bool, - print_bool - }, - { - "brightness", - "# Set the initial brightness of a video device.\n" - "# If auto_brightness is enabled, this value defines the average brightness level\n" - "# which Motion will try and adjust to.\n" - "# Valid range 0-255, default 0 = disabled", - 0, - CONF_OFFSET(brightness), - copy_int, - print_int - }, - { - "contrast", - "# Set the contrast of a video device.\n" - "# Valid range 0-255, default 0 = disabled", - 0, - CONF_OFFSET(contrast), - copy_int, - print_int - }, - { - "saturation", - "# Set the saturation of a video device.\n" - "# Valid range 0-255, default 0 = disabled", - 0, - CONF_OFFSET(saturation), - copy_int, - print_int - }, - { - "hue", - "# Set the hue of a video device (NTSC feature).\n" - "# Valid range 0-255, default 0 = disabled", - 0, - CONF_OFFSET(hue), - copy_int, - print_int - }, - { - "roundrobin_frames", - "\n############################################################\n" - "# Round Robin (multiple inputs on same video device name)\n" - "############################################################\n\n" - "# Number of frames to capture in each roundrobin step (default: 1)", - 0, - CONF_OFFSET(roundrobin_frames), - copy_int, - print_int - }, - { - "roundrobin_skip", - "# Number of frames to skip before each roundrobin step (default: 1)", - 0, - CONF_OFFSET(roundrobin_skip), - copy_int, - print_int - }, - { - "switchfilter", - "# Try to filter out noise generated by roundrobin (default: off)", - 0, - CONF_OFFSET(switchfilter), - copy_bool, - print_bool - }, - { - "threshold", - "\n############################################################\n" - "# Motion Detection Settings:\n" - "############################################################\n\n" - "# Threshold for number of changed pixels in an image that\n" - "# triggers motion detection (default: 1500)", - 0, - CONF_OFFSET(max_changes), - copy_int, - print_int - }, - { - "threshold_tune", - "# Automatically tune the threshold down if possible (default: off)", - 0, - CONF_OFFSET(threshold_tune), - copy_bool, - print_bool - }, - { - "noise_level", - "# Noise threshold for the motion detection (default: 32)", - 0, - CONF_OFFSET(noise), - copy_int, - print_int - }, - { - "noise_tune", - "# Automatically tune the noise threshold (default: on)", - 0, - CONF_OFFSET(noise_tune), - copy_bool, - print_bool - }, - { - "despeckle_filter", - "# Despeckle motion image using (e)rode or (d)ilate or (l)abel (Default: not defined)\n" - "# Recommended value is EedDl. Any combination (and number of) of E, e, d, and D is valid.\n" - "# (l)abeling must only be used once and the 'l' must be the last letter.\n" - "# Comment out to disable", - 0, - CONF_OFFSET(despeckle_filter), - copy_string, - print_string - }, - { - "area_detect", - "# Detect motion in predefined areas (1 - 9). Areas are numbered like that: 1 2 3\n" - "# A script (on_area_detected) is started immediately when motion is 4 5 6\n" - "# detected in one of the given areas, but only once during an event. 7 8 9\n" - "# One or more areas can be specified with this option. (Default: not defined)", - 0, - CONF_OFFSET(area_detect), - copy_string, - print_string - }, - { - "mask_file", - "# PGM file to use as a sensitivity mask.\n" - "# Full path name to. (Default: not defined)", - 0, - CONF_OFFSET(mask_file), - copy_string, - print_string - }, - { - "smart_mask_speed", - "# Dynamically create a mask file during operation (default: 0)\n" - "# Adjust speed of mask changes from 0 (off) to 10 (fast)", - 0, - CONF_OFFSET(smart_mask_speed), - copy_int, - print_int - }, - { - "lightswitch", - "# Ignore sudden massive light intensity changes given as a percentage of the picture\n" - "# area that changed intensity. Valid range: 0 - 100 , default: 0 = disabled", - 0, - CONF_OFFSET(lightswitch), - copy_int, - print_int - }, - { - "minimum_motion_frames", - "# Picture frames must contain motion at least the specified number of frames\n" - "# in a row before they are detected as true motion. At the default of 1, all\n" - "# motion is detected. Valid range: 1 to thousands, recommended 1-5", - 0, - CONF_OFFSET(minimum_motion_frames), - copy_int, - print_int - }, - { - "pre_capture", - "# Specifies the number of pre-captured (buffered) pictures from before motion\n" - "# was detected that will be output at motion detection.\n" - "# Recommended range: 0 to 5 (default: 0)\n" - "# Do not use large values! Large values will cause Motion to skip video frames and\n" - "# cause unsmooth movies. To smooth movies use larger values of post_capture instead.", - 0, - CONF_OFFSET(pre_capture), - copy_int, - print_int - }, - { - "post_capture", - "# Number of frames to capture after motion is no longer detected (default: 0)", - 0, - CONF_OFFSET(post_capture), - copy_int, - print_int - }, - { - "event_gap", - "# Event Gap is the seconds of no motion detection that triggers the end of an event\n" - "# An event is defined as a series of motion images taken within a short timeframe.\n" - "# Recommended value is 60 seconds (Default). The value 0 is allowed and disables\n" - "# events causing all Motion to be written to one single movie file and no pre_capture.", - 0, - CONF_OFFSET(event_gap), - copy_int, - print_int - }, - { - "max_movie_time", - "# Maximum length in seconds of a movie\n" - "# When value is exceeded a new movie file is created. (Default: 0 = infinite)", - 0, - CONF_OFFSET(max_movie_time), - copy_int, - print_int - }, - { - "emulate_motion", - "# Always save images even if there was no motion (default: off)", - 0, - CONF_OFFSET(emulate_motion), - copy_bool, - print_bool - }, - { - "output_pictures", - "\n############################################################\n" - "# Image File Output\n" - "############################################################\n\n" - "# Output 'normal' pictures when motion is detected (default: on)\n" - "# Valid values: on, off, first, best, center\n" - "# When set to 'first', only the first picture of an event is saved.\n" - "# Picture with most motion of an event is saved when set to 'best'.\n" - "# Picture with motion nearest center of picture is saved when set to 'center'.\n" - "# Can be used as preview shot for the corresponding movie.", - 0, - CONF_OFFSET(output_pictures), - copy_string, - print_string - }, - { - "output_debug_pictures", - "# Output pictures with only the pixels moving object (ghost images) (default: off)", - 0, - CONF_OFFSET(motion_img), - copy_bool, - print_bool - }, - { - "quality", - "# The quality (in percent) to be used by the jpeg compression (default: 75)", - 0, - CONF_OFFSET(quality), - copy_int, - print_int - }, - { - "picture_type", - "# Type of output images\n" - "# Valid values: jpeg, ppm (default: jpeg)", - 0, - CONF_OFFSET(picture_type), - copy_string, - print_string - }, + { + "input", + "# The video input to be used (default: 8)\n" + "# Should normally be set to 0 or 1 for video/TV cards, and 8 for USB cameras", + 0, + CONF_OFFSET(input), + copy_int, + print_int + }, + { + "norm", + "# The video norm to use (only for video capture and TV tuner cards)\n" + "# Values: 0 (PAL), 1 (NTSC), 2 (SECAM), 3 (PAL NC no colour). Default: 0 (PAL)", + 0, + CONF_OFFSET(norm), + copy_int, + print_int + }, + { + "frequency", + "# The frequency to set the tuner to (kHz) (only for TV tuner cards) (default: 0)", + 0, + CONF_OFFSET(frequency), + copy_int, + print_int + }, + { + "rotate", + "# Rotate image this number of degrees. The rotation affects all saved images as\n" + "# well as movies. Valid values: 0 (default = no rotation), 90, 180 and 270.", + 0, + CONF_OFFSET(rotate_deg), + copy_int, + print_int + }, + { + "width", + "# Image width (pixels). Valid range: Camera dependent, default: 352", + 0, + CONF_OFFSET(width), + copy_int, + print_int + }, + { + "height", + "# Image height (pixels). Valid range: Camera dependent, default: 288", + 0, + CONF_OFFSET(height), + copy_int, + print_int + }, + { + "framerate", + "# Maximum number of frames to be captured per second.\n" + "# Valid range: 2-100. Default: 100 (almost no limit).", + 0, + CONF_OFFSET(frame_limit), + copy_int, + print_int + }, + { + "minimum_frame_time", + "# Minimum time in seconds between capturing picture frames from the camera.\n" + "# Default: 0 = disabled - the capture rate is given by the camera framerate.\n" + "# This option is used when you want to capture images at a rate lower than 2 per second.", + 0, + CONF_OFFSET(minimum_frame_time), + copy_int, + print_int + }, + { + "netcam_url", + "# URL to use if you are using a network camera, size will be autodetected (incl http:// ftp:// or file:///)\n" + "# Must be a URL that returns single jpeg pictures or a raw mjpeg stream. Default: Not defined", + 0, + CONF_OFFSET(netcam_url), + copy_string, + print_string + }, + { + "netcam_userpass", + "# Username and password for network camera (only if required). Default: not defined\n" + "# Syntax is user:password", + 0, + CONF_OFFSET(netcam_userpass), + copy_string, + print_string + }, + { + "netcam_keepalive", + "# The setting for keep-alive of network socket, should improve performance on compatible net cameras.\n" + "# off : the historical implementation using HTTP/1.0, closing the socket after each http request.\n" + "# force : Use HTTP/1.0 requests with keep alive header to reuse the same connection.\n" + "# on : Use HTTP/1.1 requests that support keep alive as default.\n" + "# Default: off", + 0, + CONF_OFFSET(netcam_keepalive), + copy_string, + print_string + }, + { + "netcam_proxy", + "# URL to use for a netcam proxy server, if required, e.g. \"http://myproxy\".\n" + "# If a port number other than 80 is needed, use \"http://myproxy:1234\".\n" + "# Default: not defined", + 0, + CONF_OFFSET(netcam_proxy), + copy_string, + print_string + }, + { + "netcam_broken", + "# Set less strict jpeg checks for network cameras with a poor/buggy firmware.\n" + "# Default: off", + 0, + CONF_OFFSET(netcam_broken), + copy_bool, + print_bool + }, + { + "auto_brightness", + "# Let motion regulate the brightness of a video device (default: off).\n" + "# The auto_brightness feature uses the brightness option as its target value.\n" + "# If brightness is zero auto_brightness will adjust to average brightness value 128.\n" + "# Only recommended for cameras without auto brightness", + 0, + CONF_OFFSET(autobright), + copy_bool, + print_bool + }, + { + "brightness", + "# Set the initial brightness of a video device.\n" + "# If auto_brightness is enabled, this value defines the average brightness level\n" + "# which Motion will try and adjust to.\n" + "# Valid range 0-255, default 0 = disabled", + 0, + CONF_OFFSET(brightness), + copy_int, + print_int + }, + { + "contrast", + "# Set the contrast of a video device.\n" + "# Valid range 0-255, default 0 = disabled", + 0, + CONF_OFFSET(contrast), + copy_int, + print_int + }, + { + "saturation", + "# Set the saturation of a video device.\n" + "# Valid range 0-255, default 0 = disabled", + 0, + CONF_OFFSET(saturation), + copy_int, + print_int + }, + { + "hue", + "# Set the hue of a video device (NTSC feature).\n" + "# Valid range 0-255, default 0 = disabled", + 0, + CONF_OFFSET(hue), + copy_int, + print_int + }, + { + "roundrobin_frames", + "\n############################################################\n" + "# Round Robin (multiple inputs on same video device name)\n" + "############################################################\n\n" + "# Number of frames to capture in each roundrobin step (default: 1)", + 0, + CONF_OFFSET(roundrobin_frames), + copy_int, + print_int + }, + { + "roundrobin_skip", + "# Number of frames to skip before each roundrobin step (default: 1)", + 0, + CONF_OFFSET(roundrobin_skip), + copy_int, + print_int + }, + { + "switchfilter", + "# Try to filter out noise generated by roundrobin (default: off)", + 0, + CONF_OFFSET(switchfilter), + copy_bool, + print_bool + }, + { + "threshold", + "\n############################################################\n" + "# Motion Detection Settings:\n" + "############################################################\n\n" + "# Threshold for number of changed pixels in an image that\n" + "# triggers motion detection (default: 1500)", + 0, + CONF_OFFSET(max_changes), + copy_int, + print_int + }, + { + "threshold_tune", + "# Automatically tune the threshold down if possible (default: off)", + 0, + CONF_OFFSET(threshold_tune), + copy_bool, + print_bool + }, + { + "noise_level", + "# Noise threshold for the motion detection (default: 32)", + 0, + CONF_OFFSET(noise), + copy_int, + print_int + }, + { + "noise_tune", + "# Automatically tune the noise threshold (default: on)", + 0, + CONF_OFFSET(noise_tune), + copy_bool, + print_bool + }, + { + "despeckle_filter", + "# Despeckle motion image using (e)rode or (d)ilate or (l)abel (Default: not defined)\n" + "# Recommended value is EedDl. Any combination (and number of) of E, e, d, and D is valid.\n" + "# (l)abeling must only be used once and the 'l' must be the last letter.\n" + "# Comment out to disable", + 0, + CONF_OFFSET(despeckle_filter), + copy_string, + print_string + }, + { + "area_detect", + "# Detect motion in predefined areas (1 - 9). Areas are numbered like that: 1 2 3\n" + "# A script (on_area_detected) is started immediately when motion is 4 5 6\n" + "# detected in one of the given areas, but only once during an event. 7 8 9\n" + "# One or more areas can be specified with this option. (Default: not defined)", + 0, + CONF_OFFSET(area_detect), + copy_string, + print_string + }, + { + "mask_file", + "# PGM file to use as a sensitivity mask.\n" + "# Full path name to. (Default: not defined)", + 0, + CONF_OFFSET(mask_file), + copy_string, + print_string + }, + { + "smart_mask_speed", + "# Dynamically create a mask file during operation (default: 0)\n" + "# Adjust speed of mask changes from 0 (off) to 10 (fast)", + 0, + CONF_OFFSET(smart_mask_speed), + copy_int, + print_int + }, + { + "lightswitch", + "# Ignore sudden massive light intensity changes given as a percentage of the picture\n" + "# area that changed intensity. Valid range: 0 - 100 , default: 0 = disabled", + 0, + CONF_OFFSET(lightswitch), + copy_int, + print_int + }, + { + "minimum_motion_frames", + "# Picture frames must contain motion at least the specified number of frames\n" + "# in a row before they are detected as true motion. At the default of 1, all\n" + "# motion is detected. Valid range: 1 to thousands, recommended 1-5", + 0, + CONF_OFFSET(minimum_motion_frames), + copy_int, + print_int + }, + { + "pre_capture", + "# Specifies the number of pre-captured (buffered) pictures from before motion\n" + "# was detected that will be output at motion detection.\n" + "# Recommended range: 0 to 5 (default: 0)\n" + "# Do not use large values! Large values will cause Motion to skip video frames and\n" + "# cause unsmooth movies. To smooth movies use larger values of post_capture instead.", + 0, + CONF_OFFSET(pre_capture), + copy_int, + print_int + }, + { + "post_capture", + "# Number of frames to capture after motion is no longer detected (default: 0)", + 0, + CONF_OFFSET(post_capture), + copy_int, + print_int + }, + { + "event_gap", + "# Event Gap is the seconds of no motion detection that triggers the end of an event\n" + "# An event is defined as a series of motion images taken within a short timeframe.\n" + "# Recommended value is 60 seconds (Default). The value 0 is allowed and disables\n" + "# events causing all Motion to be written to one single movie file and no pre_capture.", + 0, + CONF_OFFSET(event_gap), + copy_int, + print_int + }, + { + "max_movie_time", + "# Maximum length in seconds of a movie\n" + "# When value is exceeded a new movie file is created. (Default: 0 = infinite)", + 0, + CONF_OFFSET(max_movie_time), + copy_int, + print_int + }, + { + "emulate_motion", + "# Always save images even if there was no motion (default: off)", + 0, + CONF_OFFSET(emulate_motion), + copy_bool, + print_bool + }, + { + "output_pictures", + "\n############################################################\n" + "# Image File Output\n" + "############################################################\n\n" + "# Output 'normal' pictures when motion is detected (default: on)\n" + "# Valid values: on, off, first, best, center\n" + "# When set to 'first', only the first picture of an event is saved.\n" + "# Picture with most motion of an event is saved when set to 'best'.\n" + "# Picture with motion nearest center of picture is saved when set to 'center'.\n" + "# Can be used as preview shot for the corresponding movie.", + 0, + CONF_OFFSET(output_pictures), + copy_string, + print_string + }, + { + "output_debug_pictures", + "# Output pictures with only the pixels moving object (ghost images) (default: off)", + 0, + CONF_OFFSET(motion_img), + copy_bool, + print_bool + }, + { + "quality", + "# The quality (in percent) to be used by the jpeg compression (default: 75)", + 0, + CONF_OFFSET(quality), + copy_int, + print_int + }, + { + "picture_type", + "# Type of output images\n" + "# Valid values: jpeg, ppm (default: jpeg)", + 0, + CONF_OFFSET(picture_type), + copy_string, + print_string + }, #ifdef HAVE_FFMPEG - { - "ffmpeg_output_movies", - "\n############################################################\n" - "# FFMPEG related options\n" - "# Film (movie) file output, and deinterlacing of the video input\n" - "# The options movie_filename and timelapse_filename are also used\n" - "# by the ffmpeg feature\n" - "############################################################\n\n" - "# Use ffmpeg to encode movies in realtime (default: off)", - 0, - CONF_OFFSET(ffmpeg_output), - copy_bool, - print_bool - }, - { - "ffmpeg_output_debug_movies", - "# Use ffmpeg to make movies with only the pixels moving\n" - "# object (ghost images) (default: off)", - 0, - CONF_OFFSET(ffmpeg_output_debug), - copy_bool, - print_bool - }, - { - "ffmpeg_timelapse", - "# Use ffmpeg to encode a timelapse movie\n" - "# Default value 0 = off - else save frame every Nth second", - 0, - CONF_OFFSET(timelapse), - copy_int, - print_int - }, - { - "ffmpeg_timelapse_mode", - "# The file rollover mode of the timelapse video\n" - "# Valid values: hourly, daily (default), weekly-sunday, weekly-monday, monthly, manual", - 0, - CONF_OFFSET(timelapse_mode), - copy_string, - print_string - }, - { - "ffmpeg_bps", - "# Bitrate to be used by the ffmpeg encoder (default: 400000)\n" - "# This option is ignored if ffmpeg_variable_bitrate is not 0 (disabled)", - 0, - CONF_OFFSET(ffmpeg_bps), - copy_int, - print_int - }, - { - "ffmpeg_variable_bitrate", - "# Enables and defines variable bitrate for the ffmpeg encoder.\n" - "# ffmpeg_bps is ignored if variable bitrate is enabled.\n" - "# Valid values: 0 (default) = fixed bitrate defined by ffmpeg_bps,\n" - "# or the range 2 - 31 where 2 means best quality and 31 is worst.", - 0, - CONF_OFFSET(ffmpeg_vbr), - copy_int, - print_int - }, - { - "ffmpeg_video_codec", - "# Codec to used by ffmpeg for the video compression.\n" - "# Timelapse movies are always made in mpeg1 format independent from this option.\n" - "# Supported formats are: mpeg1 (ffmpeg-0.4.8 only), mpeg4 (default), and msmpeg4.\n" - "# mpeg1 - gives you files with extension .mpg\n" - "# mpeg4 or msmpeg4 - gives you files with extension .avi\n" - "# msmpeg4 is recommended for use with Windows Media Player because\n" - "# it requires no installation of codec on the Windows client.\n" - "# swf - gives you a flash film with extension .swf\n" - "# flv - gives you a flash video with extension .flv\n" - "# ffv1 - FF video codec 1 for Lossless Encoding ( experimental )\n" - "# mov - QuickTime ( testing )", - 0, - CONF_OFFSET(ffmpeg_video_codec), - copy_string, - print_string - }, - { - "ffmpeg_deinterlace", - "# Use ffmpeg to deinterlace video. Necessary if you use an analog camera\n" - "# and see horizontal combing on moving objects in video or pictures.\n" - "# (default: off)", - 0, - CONF_OFFSET(ffmpeg_deinterlace), - copy_bool, - print_bool - }, + { + "ffmpeg_output_movies", + "\n############################################################\n" + "# FFMPEG related options\n" + "# Film (movie) file output, and deinterlacing of the video input\n" + "# The options movie_filename and timelapse_filename are also used\n" + "# by the ffmpeg feature\n" + "############################################################\n\n" + "# Use ffmpeg to encode movies in realtime (default: off)", + 0, + CONF_OFFSET(ffmpeg_output), + copy_bool, + print_bool + }, + { + "ffmpeg_output_debug_movies", + "# Use ffmpeg to make movies with only the pixels moving\n" + "# object (ghost images) (default: off)", + 0, + CONF_OFFSET(ffmpeg_output_debug), + copy_bool, + print_bool + }, + { + "ffmpeg_timelapse", + "# Use ffmpeg to encode a timelapse movie\n" + "# Default value 0 = off - else save frame every Nth second", + 0, + CONF_OFFSET(timelapse), + copy_int, + print_int + }, + { + "ffmpeg_timelapse_mode", + "# The file rollover mode of the timelapse video\n" + "# Valid values: hourly, daily (default), weekly-sunday, weekly-monday, monthly, manual", + 0, + CONF_OFFSET(timelapse_mode), + copy_string, + print_string + }, + { + "ffmpeg_bps", + "# Bitrate to be used by the ffmpeg encoder (default: 400000)\n" + "# This option is ignored if ffmpeg_variable_bitrate is not 0 (disabled)", + 0, + CONF_OFFSET(ffmpeg_bps), + copy_int, + print_int + }, + { + "ffmpeg_variable_bitrate", + "# Enables and defines variable bitrate for the ffmpeg encoder.\n" + "# ffmpeg_bps is ignored if variable bitrate is enabled.\n" + "# Valid values: 0 (default) = fixed bitrate defined by ffmpeg_bps,\n" + "# or the range 2 - 31 where 2 means best quality and 31 is worst.", + 0, + CONF_OFFSET(ffmpeg_vbr), + copy_int, + print_int + }, + { + "ffmpeg_video_codec", + "# Codec to used by ffmpeg for the video compression.\n" + "# Timelapse movies are always made in mpeg1 format independent from this option.\n" + "# Supported formats are: mpeg1 (ffmpeg-0.4.8 only), mpeg4 (default), and msmpeg4.\n" + "# mpeg1 - gives you files with extension .mpg\n" + "# mpeg4 or msmpeg4 - gives you files with extension .avi\n" + "# msmpeg4 is recommended for use with Windows Media Player because\n" + "# it requires no installation of codec on the Windows client.\n" + "# swf - gives you a flash film with extension .swf\n" + "# flv - gives you a flash video with extension .flv\n" + "# ffv1 - FF video codec 1 for Lossless Encoding ( experimental )\n" + "# mov - QuickTime ( testing )", + 0, + CONF_OFFSET(ffmpeg_video_codec), + copy_string, + print_string + }, + { + "ffmpeg_deinterlace", + "# Use ffmpeg to deinterlace video. Necessary if you use an analog camera\n" + "# and see horizontal combing on moving objects in video or pictures.\n" + "# (default: off)", + 0, + CONF_OFFSET(ffmpeg_deinterlace), + copy_bool, + print_bool + }, #endif /* HAVE_FFMPEG */ - { - "snapshot_interval", - "\n############################################################\n" - "# Snapshots (Traditional Periodic Webcam File Output)\n" - "############################################################\n\n" - "# Make automated snapshot every N seconds (default: 0 = disabled)", - 0, - CONF_OFFSET(snapshot_interval), - copy_int, - print_int - }, - { - "locate_motion", - "\n############################################################\n" - "# Text Display\n" - "# %Y = year, %m = month, %d = date,\n" - "# %H = hour, %M = minute, %S = second, %T = HH:MM:SS,\n" - "# %v = event, %q = frame number, %t = thread (camera) number,\n" - "# %D = changed pixels, %N = noise level, \\n = new line,\n" - "# %i and %J = width and height of motion area,\n" - "# %K and %L = X and Y coordinates of motion center\n" - "# %C = value defined by text_event - do not use with text_event!\n" - "# You can put quotation marks around the text to allow\n" - "# leading spaces\n" - "############################################################\n\n" - "# Locate and draw a box around the moving object.\n" - "# Valid values: on, off, red, preview, center, center_red (default: off)\n" - "# Set to 'redbox' will draw a red box around the moving object.\n" - "# Set to 'preview' will only draw a box in preview_shot pictures.\n" - "# Set to 'center' will draw a little cross to mark center.\n" - "# Set to 'redcross' will draw a little red cross to mark center.", - 0, - CONF_OFFSET(locate_motion), - copy_string, - print_string - }, - { - "text_right", - "# Draws the timestamp using same options as C function strftime(3)\n" - "# Default: %Y-%m-%d\\n%T = date in ISO format and time in 24 hour clock\n" - "# Text is placed in lower right corner", - 0, - CONF_OFFSET(text_right), - copy_string, - print_string - }, - { - "text_left", - "# Draw a user defined text on the images using same options as C function strftime(3)\n" - "# Default: Not defined = no text\n" - "# Text is placed in lower left corner", - 0, - CONF_OFFSET(text_left), - copy_string, - print_string - }, - { - "text_changes", - "# Draw the number of changed pixed on the images (default: off)\n" - "# Will normally be set to off except when you setup and adjust the motion settings\n" - "# Text is placed in upper right corner", - 0, - CONF_OFFSET(text_changes), - copy_bool, - print_bool - }, - { - "text_event", - "# This option defines the value of the special event conversion specifier %C\n" - "# You can use any conversion specifier in this option except %C. Date and time\n" - "# values are from the timestamp of the first image in the current event.\n" - "# Default: %Y%m%d%H%M%S\n" - "# The idea is that %C can be used filenames and text_left/right for creating\n" - "# a unique identifier for each event.", - 0, - CONF_OFFSET(text_event), - copy_string, - print_string - }, - { - "text_double", - "# Draw characters at twice normal size on images. (default: off)", - 0, - CONF_OFFSET(text_double), - copy_bool, - print_bool - }, - { - "target_dir", - "\n############################################################\n" - "# Target Directories and filenames For Images And Films\n" - "# For the options snapshot_, picture_, movie_ and timelapse_filename\n" - "# you can use conversion specifiers\n" - "# %Y = year, %m = month, %d = date,\n" - "# %H = hour, %M = minute, %S = second,\n" - "# %v = event, %q = frame number, %t = thread (camera) number,\n" - "# %D = changed pixels, %N = noise level,\n" - "# %i and %J = width and height of motion area,\n" - "# %K and %L = X and Y coordinates of motion center\n" - "# %C = value defined by text_event\n" - "# Quotation marks round string are allowed.\n" - "############################################################\n\n" - "# Target base directory for pictures and films\n" - "# Recommended to use absolute path. (Default: current working directory)", - 0, - CONF_OFFSET(filepath), - copy_string, - print_string - }, - { - "snapshot_filename", - "# File path for snapshots (jpeg or ppm) relative to target_dir\n" - "# Default: "DEF_SNAPPATH"\n" - "# Default value is equivalent to legacy oldlayout option\n" - "# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-snapshot\n" - "# File extension .jpg or .ppm is automatically added so do not include this.\n" - "# Note: A symbolic link called lastsnap.jpg created in the target_dir will always\n" - "# point to the latest snapshot, unless snapshot_filename is exactly 'lastsnap'", - 0, - CONF_OFFSET(snappath), - copy_string, - print_string - }, - { - "picture_filename", - "# File path for motion triggered images (jpeg or ppm) relative to target_dir\n" - "# Default: "DEF_IMAGEPATH"\n" - "# Default value is equivalent to legacy oldlayout option\n" - "# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-%q\n" - "# File extension .jpg or .ppm is automatically added so do not include this\n" - "# Set to 'preview' together with best-preview feature enables special naming\n" - "# convention for preview shots. See motion guide for details", - 0, - CONF_OFFSET(imagepath), - copy_string, - print_string - }, + { + "snapshot_interval", + "\n############################################################\n" + "# Snapshots (Traditional Periodic Webcam File Output)\n" + "############################################################\n\n" + "# Make automated snapshot every N seconds (default: 0 = disabled)", + 0, + CONF_OFFSET(snapshot_interval), + copy_int, + print_int + }, + { + "locate_motion", + "\n############################################################\n" + "# Text Display\n" + "# %Y = year, %m = month, %d = date,\n" + "# %H = hour, %M = minute, %S = second, %T = HH:MM:SS,\n" + "# %v = event, %q = frame number, %t = thread (camera) number,\n" + "# %D = changed pixels, %N = noise level, \\n = new line,\n" + "# %i and %J = width and height of motion area,\n" + "# %K and %L = X and Y coordinates of motion center\n" + "# %C = value defined by text_event - do not use with text_event!\n" + "# You can put quotation marks around the text to allow\n" + "# leading spaces\n" + "############################################################\n\n" + "# Locate and draw a box around the moving object.\n" + "# Valid values: on, off, red, preview, center, center_red (default: off)\n" + "# Set to 'redbox' will draw a red box around the moving object.\n" + "# Set to 'preview' will only draw a box in preview_shot pictures.\n" + "# Set to 'center' will draw a little cross to mark center.\n" + "# Set to 'redcross' will draw a little red cross to mark center.", + 0, + CONF_OFFSET(locate_motion), + copy_string, + print_string + }, + { + "text_right", + "# Draws the timestamp using same options as C function strftime(3)\n" + "# Default: %Y-%m-%d\\n%T = date in ISO format and time in 24 hour clock\n" + "# Text is placed in lower right corner", + 0, + CONF_OFFSET(text_right), + copy_string, + print_string + }, + { + "text_left", + "# Draw a user defined text on the images using same options as C function strftime(3)\n" + "# Default: Not defined = no text\n" + "# Text is placed in lower left corner", + 0, + CONF_OFFSET(text_left), + copy_string, + print_string + }, + { + "text_changes", + "# Draw the number of changed pixed on the images (default: off)\n" + "# Will normally be set to off except when you setup and adjust the motion settings\n" + "# Text is placed in upper right corner", + 0, + CONF_OFFSET(text_changes), + copy_bool, + print_bool + }, + { + "text_event", + "# This option defines the value of the special event conversion specifier %C\n" + "# You can use any conversion specifier in this option except %C. Date and time\n" + "# values are from the timestamp of the first image in the current event.\n" + "# Default: %Y%m%d%H%M%S\n" + "# The idea is that %C can be used filenames and text_left/right for creating\n" + "# a unique identifier for each event.", + 0, + CONF_OFFSET(text_event), + copy_string, + print_string + }, + { + "text_double", + "# Draw characters at twice normal size on images. (default: off)", + 0, + CONF_OFFSET(text_double), + copy_bool, + print_bool + }, + { + "target_dir", + "\n############################################################\n" + "# Target Directories and filenames For Images And Films\n" + "# For the options snapshot_, picture_, movie_ and timelapse_filename\n" + "# you can use conversion specifiers\n" + "# %Y = year, %m = month, %d = date,\n" + "# %H = hour, %M = minute, %S = second,\n" + "# %v = event, %q = frame number, %t = thread (camera) number,\n" + "# %D = changed pixels, %N = noise level,\n" + "# %i and %J = width and height of motion area,\n" + "# %K and %L = X and Y coordinates of motion center\n" + "# %C = value defined by text_event\n" + "# Quotation marks round string are allowed.\n" + "############################################################\n\n" + "# Target base directory for pictures and films\n" + "# Recommended to use absolute path. (Default: current working directory)", + 0, + CONF_OFFSET(filepath), + copy_string, + print_string + }, + { + "snapshot_filename", + "# File path for snapshots (jpeg or ppm) relative to target_dir\n" + "# Default: "DEF_SNAPPATH"\n" + "# Default value is equivalent to legacy oldlayout option\n" + "# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-snapshot\n" + "# File extension .jpg or .ppm is automatically added so do not include this.\n" + "# Note: A symbolic link called lastsnap.jpg created in the target_dir will always\n" + "# point to the latest snapshot, unless snapshot_filename is exactly 'lastsnap'", + 0, + CONF_OFFSET(snappath), + copy_string, + print_string + }, + { + "picture_filename", + "# File path for motion triggered images (jpeg or ppm) relative to target_dir\n" + "# Default: "DEF_IMAGEPATH"\n" + "# Default value is equivalent to legacy oldlayout option\n" + "# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-%q\n" + "# File extension .jpg or .ppm is automatically added so do not include this\n" + "# Set to 'preview' together with best-preview feature enables special naming\n" + "# convention for preview shots. See motion guide for details", + 0, + CONF_OFFSET(imagepath), + copy_string, + print_string + }, #ifdef HAVE_FFMPEG - { - "movie_filename", - "# File path for motion triggered ffmpeg films (movies) relative to target_dir\n" - "# Default: "DEF_MOVIEPATH"\n" - "# Default value is equivalent to legacy oldlayout option\n" - "# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H%M%S\n" - "# File extension .mpg or .avi is automatically added so do not include this\n" - "# This option was previously called ffmpeg_filename", - 0, - CONF_OFFSET(moviepath), - copy_string, - print_string - }, - { - "timelapse_filename", - "# File path for timelapse movies relative to target_dir\n" - "# Default: "DEF_TIMEPATH"\n" - "# Default value is near equivalent to legacy oldlayout option\n" - "# For Motion 3.0 compatible mode choose: %Y/%m/%d-timelapse\n" - "# File extension .mpg is automatically added so do not include this", - 0, - CONF_OFFSET(timepath), - copy_string, - print_string - }, + { + "movie_filename", + "# File path for motion triggered ffmpeg films (movies) relative to target_dir\n" + "# Default: "DEF_MOVIEPATH"\n" + "# Default value is equivalent to legacy oldlayout option\n" + "# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H%M%S\n" + "# File extension .mpg or .avi is automatically added so do not include this\n" + "# This option was previously called ffmpeg_filename", + 0, + CONF_OFFSET(moviepath), + copy_string, + print_string + }, + { + "timelapse_filename", + "# File path for timelapse movies relative to target_dir\n" + "# Default: "DEF_TIMEPATH"\n" + "# Default value is near equivalent to legacy oldlayout option\n" + "# For Motion 3.0 compatible mode choose: %Y/%m/%d-timelapse\n" + "# File extension .mpg is automatically added so do not include this", + 0, + CONF_OFFSET(timepath), + copy_string, + print_string + }, #endif /* HAVE_FFMPEG */ - { - "stream_port", - "\n############################################################\n" - "# Live Stream Server\n" - "############################################################\n\n" - "# The mini-http server listens to this port for requests (default: 0 = disabled)", - 0, - CONF_OFFSET(stream_port), - copy_int, - print_int - }, - { - "stream_quality", - "# Quality of the jpeg images produced (default: 50)", - 0, - CONF_OFFSET(stream_quality), - copy_int, - print_int - }, - { - "stream_motion", - "# Output frames at 1 fps when no motion is detected and increase to the\n" - "# rate given by stream_maxrate when motion is detected (default: off)", - 0, - CONF_OFFSET(stream_motion), - copy_bool, - print_bool - }, - { - "stream_maxrate", - "# Maximum framerate for streams (default: 1)", - 0, - CONF_OFFSET(stream_maxrate), - copy_int, - print_int - }, - { - "stream_localhost", - "# Restrict stream connections to localhost only (default: on)", - 0, - CONF_OFFSET(stream_localhost), - copy_bool, - print_bool - }, - { - "stream_limit", - "# Limits the number of images per connection (default: 0 = unlimited)\n" - "# Number can be defined by multiplying actual stream rate by desired number of seconds\n" - "# Actual stream rate is the smallest of the numbers framerate and stream_maxrate", - 0, - CONF_OFFSET(stream_limit), - copy_int, - print_int - }, - { - "webcontrol_port", - "\n############################################################\n" - "# HTTP Based Control\n" - "############################################################\n\n" - "# TCP/IP port for the http server to listen on (default: 0 = disabled)", - 1, - CONF_OFFSET(webcontrol_port), - copy_int, - print_int - }, - { - "webcontrol_localhost", - "# Restrict control connections to localhost only (default: on)", - 1, - CONF_OFFSET(webcontrol_localhost), - copy_bool, - print_bool - }, - { - "webcontrol_html_output", - "# Output for http server, select off to choose raw text plain (default: on)", - 1, - CONF_OFFSET(webcontrol_html_output), - copy_bool, - print_bool - }, - { - "webcontrol_authentication", - "# Authentication for the http based control. Syntax username:password\n" - "# Default: not defined (Disabled)", - 1, - CONF_OFFSET(webcontrol_authentication), - copy_string, - print_string - }, - { - "track_type", - "\n############################################################\n" - "# Tracking (Pan/Tilt)\n" - "############################################################\n\n" - "# Type of tracker (0=none (default), 1=stepper, 2=iomojo, 3=pwc, 4=generic, 5=uvcvideo, 6=servo)\n" - "# The generic type enables the definition of motion center and motion size to\n" - "# be used with the conversion specifiers for options like on_motion_detected", - 0, - TRACK_OFFSET(type), - copy_short, - print_short - }, - { - "track_auto", - "# Enable auto tracking (default: off)", - 0, - TRACK_OFFSET(active), - copy_bool, - print_bool - }, - { - "track_port", - "# Serial port of motor (default: none)", - 0, - TRACK_OFFSET(port), - copy_string, - print_string - }, - { - "track_motorx", - "# Motor number for x-axis (default: 0)", - 0, - TRACK_OFFSET(motorx), - copy_short, - print_short - }, - { - "track_motorx_reverse", - "# Set motorx reverse (default: off)", - 0, - TRACK_OFFSET(motorx_reverse), - copy_bool, - print_bool - }, - { - "track_motory", - "# Motor number for y-axis (default: 0)", - 0, - TRACK_OFFSET(motory), - copy_short, - print_short - }, - { - "track_motory_reverse", - "# Set motory reverse (default: off)", - 0, - TRACK_OFFSET(motory_reverse), - copy_bool, - print_bool - }, - { - "track_maxx", - "# Maximum value on x-axis (default: 0)", - 0, - TRACK_OFFSET(maxx), - copy_short, - print_short - }, - { - "track_minx", - "# Minimum value on x-axis (default: 0)", - 0, - TRACK_OFFSET(minx), - copy_short, - print_short - }, - { - "track_maxy", - "# Maximum value on y-axis (default: 0)", - 0, - TRACK_OFFSET(maxy), - copy_short, - print_short - }, - { - "track_miny", - "# Minimum value on y-axis (default: 0)", - 0, - TRACK_OFFSET(miny), - copy_short, - print_short - }, - { - "track_homex", - "# Center value on x-axis (default: 0)", - 0, - TRACK_OFFSET(homex), - copy_short, - print_short - }, - { - "track_homey", - "# Center value on y-axis (default: 0)", - 0, - TRACK_OFFSET(homey), - copy_short, - print_short - }, - { - "track_iomojo_id", - "# ID of an iomojo camera if used (default: 0)", - 0, - TRACK_OFFSET(iomojo_id), - copy_short, - print_short - }, - { - "track_step_angle_x", - "# Angle in degrees the camera moves per step on the X-axis\n" - "# with auto-track (default: 10)\n" - "# Currently only used with pwc type cameras", - 0, - TRACK_OFFSET(step_angle_x), - copy_short, - print_short - }, - { - "track_step_angle_y", - "# Angle in degrees the camera moves per step on the Y-axis\n" - "# with auto-track (default: 10)\n" - "# Currently only used with pwc type cameras", - 0, - TRACK_OFFSET(step_angle_y), - copy_short, - print_short - }, - { - "track_move_wait", - "# Delay to wait for after tracking movement as number\n" - "# of picture frames (default: 10)", - 0, - TRACK_OFFSET(move_wait), - copy_short, - print_short - }, - { - "track_speed", - "# Speed to set the motor to (stepper motor option) (default: 255)", - 0, - TRACK_OFFSET(speed), - copy_short, - print_short - }, - { - "track_stepsize", - "# Number of steps to make (stepper motor option) (default: 40)", - 0, - TRACK_OFFSET(stepsize), - copy_short, - print_short - }, - { - "quiet", - "\n############################################################\n" - "# External Commands, Warnings and Logging:\n" - "# You can use conversion specifiers for the on_xxxx commands\n" - "# %Y = year, %m = month, %d = date,\n" - "# %H = hour, %M = minute, %S = second,\n" - "# %v = event, %q = frame number, %t = thread (camera) number,\n" - "# %D = changed pixels, %N = noise level,\n" - "# %i and %J = width and height of motion area,\n" - "# %K and %L = X and Y coordinates of motion center\n" - "# %C = value defined by text_event\n" - "# %f = filename with full path\n" - "# %n = number indicating filetype\n" - "# Both %f and %n are only defined for on_picture_save,\n" - "# on_movie_start and on_movie_end\n" - "# Quotation marks round string are allowed.\n" - "############################################################\n\n" - "# Do not sound beeps when detecting motion (default: on)\n" - "# Note: Motion never beeps when running in daemon mode.", - 0, - CONF_OFFSET(quiet), - copy_bool, - print_bool - }, - { - "on_event_start", - "# Command to be executed when an event starts. (default: none)\n" - "# An event starts at first motion detected after a period of no motion defined by event_gap ", - 0, - CONF_OFFSET(on_event_start), - copy_string, - print_string - }, - { - "on_event_end", - "# Command to be executed when an event ends after a period of no motion\n" - "# (default: none). The period of no motion is defined by option event_gap.", - 0, - CONF_OFFSET(on_event_end), - copy_string, - print_string - }, - { - "on_picture_save", - "# Command to be executed when a picture (.ppm|.jpg) is saved (default: none)\n" - "# To give the filename as an argument to a command append it with %f", - 0, - CONF_OFFSET(on_picture_save), - copy_string, - print_string - }, - { - "on_motion_detected", - "# Command to be executed when a motion frame is detected (default: none)", - 0, - CONF_OFFSET(on_motion_detected), - copy_string, - print_string - }, - { - "on_area_detected", - "# Command to be executed when motion in a predefined area is detected\n" - "# Check option 'area_detect'. (default: none)", - 0, - CONF_OFFSET(on_area_detected), - copy_string, - print_string - }, + { + "stream_port", + "\n############################################################\n" + "# Live Stream Server\n" + "############################################################\n\n" + "# The mini-http server listens to this port for requests (default: 0 = disabled)", + 0, + CONF_OFFSET(stream_port), + copy_int, + print_int + }, + { + "stream_quality", + "# Quality of the jpeg images produced (default: 50)", + 0, + CONF_OFFSET(stream_quality), + copy_int, + print_int + }, + { + "stream_motion", + "# Output frames at 1 fps when no motion is detected and increase to the\n" + "# rate given by stream_maxrate when motion is detected (default: off)", + 0, + CONF_OFFSET(stream_motion), + copy_bool, + print_bool + }, + { + "stream_maxrate", + "# Maximum framerate for streams (default: 1)", + 0, + CONF_OFFSET(stream_maxrate), + copy_int, + print_int + }, + { + "stream_localhost", + "# Restrict stream connections to localhost only (default: on)", + 0, + CONF_OFFSET(stream_localhost), + copy_bool, + print_bool + }, + { + "stream_limit", + "# Limits the number of images per connection (default: 0 = unlimited)\n" + "# Number can be defined by multiplying actual stream rate by desired number of seconds\n" + "# Actual stream rate is the smallest of the numbers framerate and stream_maxrate", + 0, + CONF_OFFSET(stream_limit), + copy_int, + print_int + }, + { + "webcontrol_port", + "\n############################################################\n" + "# HTTP Based Control\n" + "############################################################\n\n" + "# TCP/IP port for the http server to listen on (default: 0 = disabled)", + 1, + CONF_OFFSET(webcontrol_port), + copy_int, + print_int + }, + { + "webcontrol_localhost", + "# Restrict control connections to localhost only (default: on)", + 1, + CONF_OFFSET(webcontrol_localhost), + copy_bool, + print_bool + }, + { + "webcontrol_html_output", + "# Output for http server, select off to choose raw text plain (default: on)", + 1, + CONF_OFFSET(webcontrol_html_output), + copy_bool, + print_bool + }, + { + "webcontrol_authentication", + "# Authentication for the http based control. Syntax username:password\n" + "# Default: not defined (Disabled)", + 1, + CONF_OFFSET(webcontrol_authentication), + copy_string, + print_string + }, + { + "track_type", + "\n############################################################\n" + "# Tracking (Pan/Tilt)\n" + "############################################################\n\n" + "# Type of tracker (0=none (default), 1=stepper, 2=iomojo, 3=pwc, 4=generic, 5=uvcvideo, 6=servo)\n" + "# The generic type enables the definition of motion center and motion size to\n" + "# be used with the conversion specifiers for options like on_motion_detected", + 0, + TRACK_OFFSET(type), + copy_short, + print_short + }, + { + "track_auto", + "# Enable auto tracking (default: off)", + 0, + TRACK_OFFSET(active), + copy_bool, + print_bool + }, + { + "track_port", + "# Serial port of motor (default: none)", + 0, + TRACK_OFFSET(port), + copy_string, + print_string + }, + { + "track_motorx", + "# Motor number for x-axis (default: 0)", + 0, + TRACK_OFFSET(motorx), + copy_short, + print_short + }, + { + "track_motorx_reverse", + "# Set motorx reverse (default: off)", + 0, + TRACK_OFFSET(motorx_reverse), + copy_bool, + print_bool + }, + { + "track_motory", + "# Motor number for y-axis (default: 0)", + 0, + TRACK_OFFSET(motory), + copy_short, + print_short + }, + { + "track_motory_reverse", + "# Set motory reverse (default: off)", + 0, + TRACK_OFFSET(motory_reverse), + copy_bool, + print_bool + }, + { + "track_maxx", + "# Maximum value on x-axis (default: 0)", + 0, + TRACK_OFFSET(maxx), + copy_short, + print_short + }, + { + "track_minx", + "# Minimum value on x-axis (default: 0)", + 0, + TRACK_OFFSET(minx), + copy_short, + print_short + }, + { + "track_maxy", + "# Maximum value on y-axis (default: 0)", + 0, + TRACK_OFFSET(maxy), + copy_short, + print_short + }, + { + "track_miny", + "# Minimum value on y-axis (default: 0)", + 0, + TRACK_OFFSET(miny), + copy_short, + print_short + }, + { + "track_homex", + "# Center value on x-axis (default: 0)", + 0, + TRACK_OFFSET(homex), + copy_short, + print_short + }, + { + "track_homey", + "# Center value on y-axis (default: 0)", + 0, + TRACK_OFFSET(homey), + copy_short, + print_short + }, + { + "track_iomojo_id", + "# ID of an iomojo camera if used (default: 0)", + 0, + TRACK_OFFSET(iomojo_id), + copy_short, + print_short + }, + { + "track_step_angle_x", + "# Angle in degrees the camera moves per step on the X-axis\n" + "# with auto-track (default: 10)\n" + "# Currently only used with pwc type cameras", + 0, + TRACK_OFFSET(step_angle_x), + copy_short, + print_short + }, + { + "track_step_angle_y", + "# Angle in degrees the camera moves per step on the Y-axis\n" + "# with auto-track (default: 10)\n" + "# Currently only used with pwc type cameras", + 0, + TRACK_OFFSET(step_angle_y), + copy_short, + print_short + }, + { + "track_move_wait", + "# Delay to wait for after tracking movement as number\n" + "# of picture frames (default: 10)", + 0, + TRACK_OFFSET(move_wait), + copy_short, + print_short + }, + { + "track_speed", + "# Speed to set the motor to (stepper motor option) (default: 255)", + 0, + TRACK_OFFSET(speed), + copy_short, + print_short + }, + { + "track_stepsize", + "# Number of steps to make (stepper motor option) (default: 40)", + 0, + TRACK_OFFSET(stepsize), + copy_short, + print_short + }, + { + "quiet", + "\n############################################################\n" + "# External Commands, Warnings and Logging:\n" + "# You can use conversion specifiers for the on_xxxx commands\n" + "# %Y = year, %m = month, %d = date,\n" + "# %H = hour, %M = minute, %S = second,\n" + "# %v = event, %q = frame number, %t = thread (camera) number,\n" + "# %D = changed pixels, %N = noise level,\n" + "# %i and %J = width and height of motion area,\n" + "# %K and %L = X and Y coordinates of motion center\n" + "# %C = value defined by text_event\n" + "# %f = filename with full path\n" + "# %n = number indicating filetype\n" + "# Both %f and %n are only defined for on_picture_save,\n" + "# on_movie_start and on_movie_end\n" + "# Quotation marks round string are allowed.\n" + "############################################################\n\n" + "# Do not sound beeps when detecting motion (default: on)\n" + "# Note: Motion never beeps when running in daemon mode.", + 0, + CONF_OFFSET(quiet), + copy_bool, + print_bool + }, + { + "on_event_start", + "# Command to be executed when an event starts. (default: none)\n" + "# An event starts at first motion detected after a period of no motion defined by event_gap ", + 0, + CONF_OFFSET(on_event_start), + copy_string, + print_string + }, + { + "on_event_end", + "# Command to be executed when an event ends after a period of no motion\n" + "# (default: none). The period of no motion is defined by option event_gap.", + 0, + CONF_OFFSET(on_event_end), + copy_string, + print_string + }, + { + "on_picture_save", + "# Command to be executed when a picture (.ppm|.jpg) is saved (default: none)\n" + "# To give the filename as an argument to a command append it with %f", + 0, + CONF_OFFSET(on_picture_save), + copy_string, + print_string + }, + { + "on_motion_detected", + "# Command to be executed when a motion frame is detected (default: none)", + 0, + CONF_OFFSET(on_motion_detected), + copy_string, + print_string + }, + { + "on_area_detected", + "# Command to be executed when motion in a predefined area is detected\n" + "# Check option 'area_detect'. (default: none)", + 0, + CONF_OFFSET(on_area_detected), + copy_string, + print_string + }, #ifdef HAVE_FFMPEG - { - "on_movie_start", - "# Command to be executed when a movie file (.mpg|.avi) is created. (default: none)\n" - "# To give the filename as an argument to a command append it with %f", - 0, - CONF_OFFSET(on_movie_start), - copy_string, - print_string - }, - { - "on_movie_end", - "# Command to be executed when a movie file (.mpg|.avi) is closed. (default: none)\n" - "# To give the filename as an argument to a command append it with %f", - 0, - CONF_OFFSET(on_movie_end), - copy_string, - print_string - }, + { + "on_movie_start", + "# Command to be executed when a movie file (.mpg|.avi) is created. (default: none)\n" + "# To give the filename as an argument to a command append it with %f", + 0, + CONF_OFFSET(on_movie_start), + copy_string, + print_string + }, + { + "on_movie_end", + "# Command to be executed when a movie file (.mpg|.avi) is closed. (default: none)\n" + "# To give the filename as an argument to a command append it with %f", + 0, + CONF_OFFSET(on_movie_end), + copy_string, + print_string + }, #endif /* HAVE_FFMPEG */ - { - "on_camera_lost", - "# Command to be executed when a camera can't be opened or if it is lost\n" - "# NOTE: There is situations when motion don't detect a lost camera!\n" - "# It depends on the driver, some drivers dosn't detect a lost camera at all\n" - "# Some hangs the motion thread. Some even hangs the PC! (default: none)\n", - 0, - CONF_OFFSET(on_camera_lost), - copy_string, - print_string - }, + { + "on_camera_lost", + "# Command to be executed when a camera can't be opened or if it is lost\n" + "# NOTE: There is situations when motion don't detect a lost camera!\n" + "# It depends on the driver, some drivers dosn't detect a lost camera at all\n" + "# Some hangs the motion thread. Some even hangs the PC! (default: none)\n", + 0, + CONF_OFFSET(on_camera_lost), + copy_string, + print_string + }, #if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) - { - "sql_log_picture", - "\n############################################################\n" - "# Common Options For MySQL and PostgreSQL database features.\n" - "# Options require the MySQL/PostgreSQL options to be active also.\n" - "############################################################\n\n" - "# Log to the database when creating motion triggered image file (default: on)", - 0, - CONF_OFFSET(sql_log_image), - copy_bool, - print_bool - }, - { - "sql_log_snapshot", - "# Log to the database when creating a snapshot image file (default: on)", - 0, - CONF_OFFSET(sql_log_snapshot), - copy_bool, - print_bool - }, - { - "sql_log_movie", - "# Log to the database when creating motion triggered movie file (default: off)", - 0, - CONF_OFFSET(sql_log_movie), - copy_bool, - print_bool - }, - { - "sql_log_timelapse", - "# Log to the database when creating timelapse movie file (default: off)", - 0, - CONF_OFFSET(sql_log_timelapse), - copy_bool, - print_bool - }, - { - "sql_query", - "# SQL query string that is sent to the database\n" - "# Use same conversion specifiers has for text features\n" - "# Additional special conversion specifiers are\n" - "# %n = the number representing the file_type\n" - "# %f = filename with full path\n" - "# Default value:\n" - "# insert into security(camera, filename, frame, file_type, time_stamp, text_event) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C')", - 0, - CONF_OFFSET(sql_query), - copy_string, - print_string - }, - { - "database_type", - "\n############################################################\n" - "# Database Options \n" - "############################################################\n\n" - "# database type : mysql, postgresql (default : not defined)", - 0, - CONF_OFFSET(database_type), - copy_string, - print_string - }, - { - "database_dbname", - "# database to log to (default: not defined)", - 0, - CONF_OFFSET(database_dbname), - copy_string, - print_string - }, - { - "database_host", - "# The host on which the database is located (default: not defined)", - 0, - CONF_OFFSET(database_host), - copy_string, - print_string - }, - { - "database_user", - "# User account name for database (default: not defined)", - 0, - CONF_OFFSET(database_user), - copy_string, - print_string - }, - { - "database_password", - "# User password for database (default: not defined)", - 0, - CONF_OFFSET(database_password), - copy_string, - print_string - }, - { - "database_port", - "# Port on which the database is located (default: not defined)\n" - "# mysql 3306 , postgresql 5432 (default: not defined)", - 0, - CONF_OFFSET(database_port), - copy_int, - print_int - }, + { + "sql_log_picture", + "\n############################################################\n" + "# Common Options For MySQL and PostgreSQL database features.\n" + "# Options require the MySQL/PostgreSQL options to be active also.\n" + "############################################################\n\n" + "# Log to the database when creating motion triggered image file (default: on)", + 0, + CONF_OFFSET(sql_log_image), + copy_bool, + print_bool + }, + { + "sql_log_snapshot", + "# Log to the database when creating a snapshot image file (default: on)", + 0, + CONF_OFFSET(sql_log_snapshot), + copy_bool, + print_bool + }, + { + "sql_log_movie", + "# Log to the database when creating motion triggered movie file (default: off)", + 0, + CONF_OFFSET(sql_log_movie), + copy_bool, + print_bool + }, + { + "sql_log_timelapse", + "# Log to the database when creating timelapse movie file (default: off)", + 0, + CONF_OFFSET(sql_log_timelapse), + copy_bool, + print_bool + }, + { + "sql_query", + "# SQL query string that is sent to the database\n" + "# Use same conversion specifiers has for text features\n" + "# Additional special conversion specifiers are\n" + "# %n = the number representing the file_type\n" + "# %f = filename with full path\n" + "# Default value:\n" + "# insert into security(camera, filename, frame, file_type, time_stamp, text_event) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C')", + 0, + CONF_OFFSET(sql_query), + copy_string, + print_string + }, + { + "database_type", + "\n############################################################\n" + "# Database Options \n" + "############################################################\n\n" + "# database type : mysql, postgresql (default : not defined)", + 0, + CONF_OFFSET(database_type), + copy_string, + print_string + }, + { + "database_dbname", + "# database to log to (default: not defined)", + 0, + CONF_OFFSET(database_dbname), + copy_string, + print_string + }, + { + "database_host", + "# The host on which the database is located (default: not defined)", + 0, + CONF_OFFSET(database_host), + copy_string, + print_string + }, + { + "database_user", + "# User account name for database (default: not defined)", + 0, + CONF_OFFSET(database_user), + copy_string, + print_string + }, + { + "database_password", + "# User password for database (default: not defined)", + 0, + CONF_OFFSET(database_password), + copy_string, + print_string + }, + { + "database_port", + "# Port on which the database is located (default: not defined)\n" + "# mysql 3306 , postgresql 5432 (default: not defined)", + 0, + CONF_OFFSET(database_port), + copy_int, + print_int + }, #endif /* defined(HAVE_MYSQL) || defined(HAVE_PGSQL) */ - { - "video_pipe", - "\n############################################################\n" - "# Video Loopback Device (vloopback project)\n" - "############################################################\n\n" - "# Output images to a video4linux loopback device\n" - "# The value '-' means next available (default: not defined)", - 0, - CONF_OFFSET(vidpipe), - copy_string, - print_string - }, - { - "motion_video_pipe", - "# Output motion images to a video4linux loopback device\n" - "# The value '-' means next available (default: not defined)", - 0, - CONF_OFFSET(motionvidpipe), - copy_string, - print_string - }, - { - "thread", - "\n##############################################################\n" - "# Thread config files - One for each camera.\n" - "# Except if only one camera - You only need this config file.\n" - "# If you have more than one camera you MUST define one thread\n" - "# config file for each camera in addition to this config file.\n" - "##############################################################\n", - 1, - 0, - config_thread, - print_thread - }, -{ NULL, NULL, 0, 0, NULL, NULL } + { + "video_pipe", + "\n############################################################\n" + "# Video Loopback Device (vloopback project)\n" + "############################################################\n\n" + "# Output images to a video4linux loopback device\n" + "# The value '-' means next available (default: not defined)", + 0, + CONF_OFFSET(vidpipe), + copy_string, + print_string + }, + { + "motion_video_pipe", + "# Output motion images to a video4linux loopback device\n" + "# The value '-' means next available (default: not defined)", + 0, + CONF_OFFSET(motionvidpipe), + copy_string, + print_string + }, + { + "thread", + "\n##############################################################\n" + "# Thread config files - One for each camera.\n" + "# Except if only one camera - You only need this config file.\n" + "# If you have more than one camera you MUST define one thread\n" + "# config file for each camera in addition to this config file.\n" + "##############################################################\n", + 1, + 0, + config_thread, + print_thread + }, + { NULL, NULL, 0, 0, NULL, NULL } }; /* conf_cmdline sets the conf struct options as defined by the command line. @@ -1380,38 +1380,41 @@ config_param config_params[] = { */ static void conf_cmdline (struct context *cnt, short int thread) { - struct config *conf = &cnt->conf; - int c; - - /* For the string options, we free() if necessary and malloc() - * if necessary. This is accomplished by calling mystrcpy(); - * see this function for more information. - */ - while ((c = getopt(conf->argc, conf->argv, "c:d:hns?p:")) != EOF) - switch (c) { - case 'c': - if (thread == -1) strcpy(cnt->conf_filename, optarg); - break; - case 'n': - cnt->daemon = 0; - break; - case 's': - conf->setup_mode = 1; - break; - case 'd': - /* no validation - just take what user gives */ - debug_level = (unsigned short int)atoi(optarg); - break; - case 'p': - if (thread == -1) strcpy(cnt->pid_file, optarg); - break; - case 'h': - case '?': - default: - usage(); - exit(1); - } - optind = 1; + struct config *conf = &cnt->conf; + int c; + + /* For the string options, we free() if necessary and malloc() + * if necessary. This is accomplished by calling mystrcpy(); + * see this function for more information. + */ + while ((c = getopt(conf->argc, conf->argv, "c:d:hns?p:")) != EOF) + switch (c) { + case 'c': + if (thread == -1) + strcpy(cnt->conf_filename, optarg); + break; + case 'n': + cnt->daemon = 0; + break; + case 's': + conf->setup_mode = 1; + break; + case 'd': + /* no validation - just take what user gives */ + debug_level = (unsigned short int)atoi(optarg); + break; + case 'p': + if (thread == -1) + strcpy(cnt->pid_file, optarg); + break; + case 'h': + case '?': + default: + usage(); + exit(1); + } + + optind = 1; } @@ -1423,45 +1426,45 @@ static void conf_cmdline (struct context *cnt, short int thread) */ struct context **conf_cmdparse(struct context **cnt, const char *cmd, const char *arg1) { - unsigned short int i = 0; - - if(!cmd) - return cnt; - - /* We search through config_params until we find a param_name that matches - * our option given by cmd (or reach the end = NULL) - */ - while( config_params[i].param_name != NULL ) { - if(!strncasecmp(cmd, config_params[i].param_name , 255 + 50)) { // Why +50? - - /* if config_param is string we don't want to check arg1 */ - if (strcmp(config_type(&config_params[i]), "string")) { - if(config_params[i].conf_value && !arg1) - return cnt; - } - - /* We call the function given by the pointer config_params[i].copy - * If the option is a bool, copy_bool is called. - * If the option is an int, copy_int is called. - * If the option is a short, copy_short is called. - * If the option is a string, copy_string is called. - * If the option is a thread, config_thread is called. - * The arguments to the function are: - * cnt - a pointer to the context structure - * arg1 - a pointer to the new option value (represented as string) - * config_params[i].conf_value - an integer value which is a pointer - * to the context structure member relative to the pointer cnt. - */ - cnt = config_params[i].copy( cnt, arg1, config_params[i].conf_value); - return cnt; - } - i++; - } - - /* We reached the end of config_params without finding a matching option */ - motion_log(LOG_ERR, 0, "%s: Unknown config option \"%s\"", __FUNCTION__, cmd); - - return cnt; + unsigned short int i = 0; + + if (!cmd) + return cnt; + + /* We search through config_params until we find a param_name that matches + * our option given by cmd (or reach the end = NULL) + */ + while (config_params[i].param_name != NULL) { + if (!strncasecmp(cmd, config_params[i].param_name , 255 + 50)) { // Why +50? + + /* if config_param is string we don't want to check arg1 */ + if (strcmp(config_type(&config_params[i]), "string")) { + if (config_params[i].conf_value && !arg1) + return cnt; + } + + /* We call the function given by the pointer config_params[i].copy + * If the option is a bool, copy_bool is called. + * If the option is an int, copy_int is called. + * If the option is a short, copy_short is called. + * If the option is a string, copy_string is called. + * If the option is a thread, config_thread is called. + * The arguments to the function are: + * cnt - a pointer to the context structure + * arg1 - a pointer to the new option value (represented as string) + * config_params[i].conf_value - an integer value which is a pointer + * to the context structure member relative to the pointer cnt. + */ + cnt = config_params[i].copy(cnt, arg1, config_params[i].conf_value); + return cnt; + } + i++; + } + + /* We reached the end of config_params without finding a matching option */ + motion_log(LOG_ERR, 0, "%s: Unknown config option \"%s\"", __FUNCTION__, cmd); + + return cnt; } /* conf_process walks through an already open config file line by line @@ -1476,66 +1479,66 @@ struct context **conf_cmdparse(struct context **cnt, const char *cmd, const char */ static struct context **conf_process(struct context **cnt, FILE *fp) { - /* process each line from the config file */ - - char line[PATH_MAX], *cmd = NULL, *arg1 = NULL; - char *beg = NULL, *end = NULL; - - while (fgets(line, PATH_MAX-1, fp)) { - if(!(line[0]=='#' || line[0]==';' || strlen(line)< 2)) {/* skipcomment */ - - arg1 = NULL; - - /* trim white space and any CR or LF at the end of the line */ - end = line + strlen(line) - 1; /* Point to the last non-null character in the string */ - while (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r') { - end--; - } - *(end+1) = '\0'; - - /* If line is only whitespace we continue to the next line */ - if (strlen(line) == 0) - continue; - - /* trim leading whitespace from the line and find command */ - beg = line; - while (*beg == ' ' || *beg == '\t') { - beg++; - } - - cmd = beg; /* command starts here */ - - while (*beg != ' ' && *beg != '\t' && *beg != '=' && *beg != '\0') { - beg++; - } - *beg = '\0'; /* command string terminates here */ - - /* trim space between command and argument */ - beg++; - - if (strlen(beg) > 0) { - while (*beg == ' ' || *beg == '\t' || *beg == '=' || *beg == '\n' || *beg == '\r') { - beg++; - } - - /* If argument is in "" we will strip them off - It is important that we can use "" so that we can use - leading spaces in text_left and text_right */ - if ((beg[0]=='"' && beg[strlen(beg)-1]=='"') || - (beg[0]=='\'' && beg[strlen(beg)-1]=='\'')) { - beg[strlen(beg)-1]='\0'; - beg++; - } - - arg1 = beg; /* Argument starts here */ - } - /* else arg1 stays null pointer */ - - cnt = conf_cmdparse(cnt, cmd, arg1); - } - } - - return cnt; + /* process each line from the config file */ + + char line[PATH_MAX], *cmd = NULL, *arg1 = NULL; + char *beg = NULL, *end = NULL; + + while (fgets(line, PATH_MAX-1, fp)) { + if (!(line[0] == '#' || line[0] == ';' || strlen(line) < 2)) {/* skipcomment */ + + arg1 = NULL; + + /* trim white space and any CR or LF at the end of the line */ + end = line + strlen(line) - 1; /* Point to the last non-null character in the string */ + while (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r') + end--; + + *(end+1) = '\0'; + + /* If line is only whitespace we continue to the next line */ + if (strlen(line) == 0) + continue; + + /* trim leading whitespace from the line and find command */ + beg = line; + while (*beg == ' ' || *beg == '\t') + beg++; + + + cmd = beg; /* command starts here */ + + while (*beg != ' ' && *beg != '\t' && *beg != '=' && *beg != '\0') + beg++; + + *beg = '\0'; /* command string terminates here */ + + /* trim space between command and argument */ + beg++; + + if (strlen(beg) > 0) { + while (*beg == ' ' || *beg == '\t' || *beg == '=' || *beg == '\n' || *beg == '\r') + beg++; + + + /* If argument is in "" we will strip them off + It is important that we can use "" so that we can use + leading spaces in text_left and text_right */ + if ((beg[0] == '"' && beg[strlen(beg)-1] == '"') || + (beg[0] == '\'' && beg[strlen(beg)-1] == '\'')) { + beg[strlen(beg)-1] = '\0'; + beg++; + } + + arg1 = beg; /* Argument starts here */ + } + /* else arg1 stays null pointer */ + + cnt = conf_cmdparse(cnt, cmd, arg1); + } + } + + return cnt; } @@ -1544,55 +1547,61 @@ static struct context **conf_process(struct context **cnt, FILE *fp) */ void conf_print(struct context **cnt) { - const char *retval; - char *val; - unsigned short int i, thread; - FILE *conffile; - - for (thread = 0; cnt[thread]; thread++) { - motion_log(LOG_INFO, 0, "%s: Writing config file to %s", - __FUNCTION__, cnt[thread]->conf_filename); - conffile = myfopen(cnt[thread]->conf_filename, "w"); - if (!conffile) - continue; - fprintf(conffile, "# %s\n", cnt[thread]->conf_filename); - fprintf(conffile, "#\n# This config file was generated by motion " VERSION "\n"); - fprintf(conffile, "\n\n"); - for (i = 0; config_params[i].param_name; i++) { - retval = config_params[i].print(cnt, NULL, i, thread); - /*If config parameter has a value (not NULL) print it to the config file*/ - if (retval) { - fprintf(conffile, "%s\n", config_params[i].param_help); - /* If the option is a text_* and first char is a space put - quotation marks around to allow leading spaces */ - if (strncmp(config_params[i].param_name, "text", 4) || strncmp(retval, " ", 1)) - fprintf(conffile, "%s %s\n\n", config_params[i].param_name, retval); - else - fprintf(conffile, "%s \"%s\"\n\n", config_params[i].param_name, retval); - } else { - val = NULL; - config_params[i].print(cnt, &val, i, thread); - /* It can either be a thread file parameter or a disabled parameter - If it is a thread parameter write it out - Else write the disabled option to the config file but with a - comment mark in front of the parameter name */ - if (val) { - fprintf(conffile, "%s\n", config_params[i].param_help); - fprintf(conffile, "%s\n", val); - if (strlen(val) == 0) - fprintf(conffile, "; thread /usr/local/etc/thread1.conf\n"); - free(val); - } else if (thread == 0) { - fprintf(conffile, "%s\n", config_params[i].param_help); - fprintf(conffile, "; %s value\n\n", config_params[i].param_name); - } - } - } - - fprintf(conffile, "\n"); - fclose(conffile); - conffile = NULL; - } + const char *retval; + char *val; + unsigned short int i, thread; + FILE *conffile; + + for (thread = 0; cnt[thread]; thread++) { + motion_log(LOG_INFO, 0, "%s: Writing config file to %s", + __FUNCTION__, cnt[thread]->conf_filename); + + conffile = myfopen(cnt[thread]->conf_filename, "w"); + + if (!conffile) + continue; + + fprintf(conffile, "# %s\n", cnt[thread]->conf_filename); + fprintf(conffile, "#\n# This config file was generated by motion " VERSION "\n"); + fprintf(conffile, "\n\n"); + + for (i = 0; config_params[i].param_name; i++) { + retval = config_params[i].print(cnt, NULL, i, thread); + /*If config parameter has a value (not NULL) print it to the config file*/ + if (retval) { + fprintf(conffile, "%s\n", config_params[i].param_help); + /* If the option is a text_* and first char is a space put + quotation marks around to allow leading spaces */ + if (strncmp(config_params[i].param_name, "text", 4) || strncmp(retval, " ", 1)) + fprintf(conffile, "%s %s\n\n", config_params[i].param_name, retval); + else + fprintf(conffile, "%s \"%s\"\n\n", config_params[i].param_name, retval); + } else { + val = NULL; + config_params[i].print(cnt, &val, i, thread); + /* It can either be a thread file parameter or a disabled parameter + If it is a thread parameter write it out + Else write the disabled option to the config file but with a + comment mark in front of the parameter name */ + if (val) { + fprintf(conffile, "%s\n", config_params[i].param_help); + fprintf(conffile, "%s\n", val); + + if (strlen(val) == 0) + fprintf(conffile, "; thread /usr/local/etc/thread1.conf\n"); + + free(val); + } else if (thread == 0) { + fprintf(conffile, "%s\n", config_params[i].param_help); + fprintf(conffile, "; %s value\n\n", config_params[i].param_name); + } + } + } + + fprintf(conffile, "\n"); + fclose(conffile); + conffile = NULL; + } } /************************************************************************** @@ -1613,104 +1622,112 @@ void conf_print(struct context **cnt) * for each thread cnt[i] so that the command line options overrides any * option given by motion.conf or a thread config file. **************************************************************************/ -struct context ** conf_load (struct context **cnt) +struct context ** conf_load(struct context **cnt) { - FILE *fp = NULL; - char filename[PATH_MAX]; - int i; - /* We preserve argc and argv because they get overwritten by the memcpy command */ - char **argv = cnt[0]->conf.argv; - int argc = cnt[0]->conf.argc; - - /* Copy the template config structure with all the default config values - * into cnt[0]->conf - */ - memcpy(&cnt[0]->conf, &conf_template, sizeof(struct config)); - - /* For each member of cnt[0] which is a pointer to a string - * if the member points to a string in conf_template and is not NULL - * 1. Reserve (malloc) memory for the string - * 2. Copy the conf_template given string to the reserved memory - * 3. Change the cnt[0] member (char*) pointing to the string in reserved memory - * This ensures that we can free and malloc the string when changed - * via http remote control or config file or command line options - */ - malloc_strings(cnt[0]); - - /* Restore the argc and argv */ - cnt[0]->conf.argv = argv; - cnt[0]->conf.argc = argc; - - /* Open the motion.conf file. We try in this sequence: - * 1. commandline - * 2. current working directory - * 3. $HOME/.motion/motion.conf - * 4. sysconfig/motion.conf - */ - /* Get filename & pid file from commandline */ - cnt[0]->conf_filename[0] = 0; - cnt[0]->pid_file[0] = 0; - - conf_cmdline(cnt[0], -1); - if (cnt[0]->conf_filename[0]) { /* User has supplied filename on commandline*/ - strcpy(filename, cnt[0]->conf_filename); - fp = fopen (filename, "r"); - } - if (!fp) { /* Commandline didn't work, try current dir */ - char *path = NULL; - if (cnt[0]->conf_filename[0]) - motion_log(-1, 1, "%s: Configfile %s not found - trying defaults.", - __FUNCTION__, filename); - if ( (path = get_current_dir_name()) == NULL) { - motion_log(LOG_ERR, 1, "%s: Error get_current_dir_name", __FUNCTION__); - exit(-1); - } - snprintf(filename, PATH_MAX, "%s/motion.conf", path); - fp = fopen (filename, "r"); - free(path); - } - if (!fp) { /* specified file does not exist... try default file */ - snprintf(filename, PATH_MAX, "%s/.motion/motion.conf", getenv("HOME")); - fp = fopen(filename, "r"); - if (!fp) { - snprintf(filename, PATH_MAX, "%s/motion.conf", sysconfdir); - fp = fopen(filename, "r"); - if (!fp) /* there is no config file.... use defaults */ - motion_log(-1, 1, "%s: could not open configfile %s", - __FUNCTION__, filename); - } - } - - /* Now we process the motion.conf config file and close it*/ - if (fp) { - strcpy(cnt[0]->conf_filename, filename); - motion_log(LOG_INFO, 0, "%s: Processing thread 0 - config file %s", - __FUNCTION__, filename); - cnt = conf_process(cnt, fp); - fclose(fp); - } else { - motion_log(LOG_INFO, 0, "%s: Not config file to process using default values", - __FUNCTION__); - } - - - /* For each thread (given by cnt[i]) being not null - * cnt is an array of pointers to a context type structure - * cnt[0] is the default context structure - * cnt[1], cnt[2], ... are context structures for each thread - * Command line options always wins over config file options - * so we go through each thread and overrides any set command line - * options - */ - i = -1; - while(cnt[++i]) - conf_cmdline(cnt[i], i); - - /* if pid file was passed from command line copy to main thread conf struct */ - if (cnt[0]->pid_file[0]) - cnt[0]->conf.pid_file = mystrcpy(cnt[0]->conf.pid_file, cnt[0]->pid_file); - - return cnt; + FILE *fp = NULL; + char filename[PATH_MAX]; + int i; + /* We preserve argc and argv because they get overwritten by the memcpy command */ + char **argv = cnt[0]->conf.argv; + int argc = cnt[0]->conf.argc; + + /* Copy the template config structure with all the default config values + * into cnt[0]->conf + */ + memcpy(&cnt[0]->conf, &conf_template, sizeof(struct config)); + + /* For each member of cnt[0] which is a pointer to a string + * if the member points to a string in conf_template and is not NULL + * 1. Reserve (malloc) memory for the string + * 2. Copy the conf_template given string to the reserved memory + * 3. Change the cnt[0] member (char*) pointing to the string in reserved memory + * This ensures that we can free and malloc the string when changed + * via http remote control or config file or command line options + */ + malloc_strings(cnt[0]); + + /* Restore the argc and argv */ + cnt[0]->conf.argv = argv; + cnt[0]->conf.argc = argc; + + /* Open the motion.conf file. We try in this sequence: + * 1. commandline + * 2. current working directory + * 3. $HOME/.motion/motion.conf + * 4. sysconfig/motion.conf + */ + /* Get filename & pid file from commandline */ + cnt[0]->conf_filename[0] = 0; + cnt[0]->pid_file[0] = 0; + + conf_cmdline(cnt[0], -1); + + if (cnt[0]->conf_filename[0]) { /* User has supplied filename on commandline*/ + strcpy(filename, cnt[0]->conf_filename); + fp = fopen (filename, "r"); + } + + if (!fp) { /* Commandline didn't work, try current dir */ + char *path = NULL; + + if (cnt[0]->conf_filename[0]) + motion_log(-1, 1, "%s: Configfile %s not found - trying defaults.", + __FUNCTION__, filename); + + if ((path = get_current_dir_name()) == NULL) { + motion_log(LOG_ERR, 1, "%s: Error get_current_dir_name", __FUNCTION__); + exit(-1); + } + + snprintf(filename, PATH_MAX, "%s/motion.conf", path); + fp = fopen (filename, "r"); + free(path); + } + if (!fp) { /* specified file does not exist... try default file */ + snprintf(filename, PATH_MAX, "%s/.motion/motion.conf", getenv("HOME")); + fp = fopen(filename, "r"); + + if (!fp) { + snprintf(filename, PATH_MAX, "%s/motion.conf", sysconfdir); + fp = fopen(filename, "r"); + + if (!fp) /* there is no config file.... use defaults */ + motion_log(-1, 1, "%s: could not open configfile %s", + __FUNCTION__, filename); + } + } + + /* Now we process the motion.conf config file and close it*/ + if (fp) { + strcpy(cnt[0]->conf_filename, filename); + motion_log(LOG_INFO, 0, "%s: Processing thread 0 - config file %s", + __FUNCTION__, filename); + cnt = conf_process(cnt, fp); + fclose(fp); + } else { + motion_log(LOG_INFO, 0, "%s: Not config file to process using default values", + __FUNCTION__); + } + + + /* For each thread (given by cnt[i]) being not null + * cnt is an array of pointers to a context type structure + * cnt[0] is the default context structure + * cnt[1], cnt[2], ... are context structures for each thread + * Command line options always wins over config file options + * so we go through each thread and overrides any set command line + * options + */ + i = -1; + + while (cnt[++i]) + conf_cmdline(cnt[i], i); + + /* if pid file was passed from command line copy to main thread conf struct */ + if (cnt[0]->pid_file[0]) + cnt[0]->conf.pid_file = mystrcpy(cnt[0]->conf.pid_file, cnt[0]->pid_file); + + return cnt; } /* malloc_strings goes through the members of a context structure. @@ -1723,22 +1740,22 @@ struct context ** conf_load (struct context **cnt) */ void malloc_strings (struct context * cnt) { - unsigned short int i = 0; - char **val; - while( config_params[i].param_name != NULL ) { - if (config_params[i].copy == copy_string) { /* if member is a string */ - /* val is made to point to a pointer to the current string */ - val = (char **)((char *)cnt+config_params[i].conf_value); - - /* if there is a string, malloc() space for it, copy - * the string to new space, and point to the new - * string. we don't free() because we're copying a - * static string. - */ - *val = mystrdup(*val); - } - i++; - } + unsigned short int i = 0; + char **val; + while (config_params[i].param_name != NULL) { + if (config_params[i].copy == copy_string) { /* if member is a string */ + /* val is made to point to a pointer to the current string */ + val = (char **)((char *)cnt+config_params[i].conf_value); + + /* if there is a string, malloc() space for it, copy + * the string to new space, and point to the new + * string. we don't free() because we're copying a + * static string. + */ + *val = mystrdup(*val); + } + i++; + } } /************************************************************************ @@ -1772,21 +1789,24 @@ void malloc_strings (struct context * cnt) */ static struct context **copy_bool (struct context **cnt, const char *str, int val_ptr) { - void *tmp; - int i; - - i=-1; - while(cnt[++i]) { - tmp = (char *)cnt[i]+(int)val_ptr; - if ( !strcmp(str, "1") || !strcasecmp(str, "yes") || !strcasecmp(str, "on")) { - *((int *)tmp) = 1; - } else { - *((int *)tmp) = 0; - } - if (cnt[0]->threadnr) - return cnt; - } - return cnt; + void *tmp; + int i; + + i = -1; + while (cnt[++i]) { + tmp = (char *)cnt[i]+(int)val_ptr; + + if (!strcmp(str, "1") || !strcasecmp(str, "yes") || !strcasecmp(str, "on")) { + *((int *)tmp) = 1; + } else { + *((int *)tmp) = 0; + } + + if (cnt[0]->threadnr) + return cnt; + } + + return cnt; } /* copy_int assigns a config option to a new integer value. @@ -1794,17 +1814,19 @@ static struct context **copy_bool (struct context **cnt, const char *str, int va */ static struct context ** copy_int(struct context **cnt, const char *str, int val_ptr) { - void *tmp; - int i; - - i=-1; - while(cnt[++i]) { - tmp = (char *)cnt[i]+val_ptr; - *((int *)tmp) = atoi(str); - if (cnt[0]->threadnr) - return cnt; - } - return cnt; + void *tmp; + int i; + + i = -1; + while (cnt[++i]) { + tmp = (char *)cnt[i]+val_ptr; + *((int *)tmp) = atoi(str); + + if (cnt[0]->threadnr) + return cnt; + } + + return cnt; } /* copy_short assigns a config option to a new short value. @@ -1812,17 +1834,20 @@ static struct context ** copy_int(struct context **cnt, const char *str, int val */ static struct context ** copy_short(struct context **cnt, const char *str, int val_ptr) { - void *tmp; - int i; - - i=-1; - while(cnt[++i]) { - tmp = (char *)cnt[i]+val_ptr; - *((short int *)tmp) = atoi(str); - if (cnt[0]->threadnr) - return cnt; - } - return cnt; + void *tmp; + int i; + + i = -1; + + while (cnt[++i]) { + tmp = (char *)cnt[i]+val_ptr; + *((short int *)tmp) = atoi(str); + + if (cnt[0]->threadnr) + return cnt; + } + + return cnt; } /* copy_string assigns a new string value to a config option. @@ -1834,25 +1859,27 @@ static struct context ** copy_short(struct context **cnt, const char *str, int v */ struct context **copy_string(struct context **cnt, const char *str, int val_ptr) { - char **tmp; - int i; - - i=-1; - while(cnt[++i]) { - tmp = (char **)((char *)cnt[i] + val_ptr); - - /* mystrcpy assigns the new string value - * including free'ing and reserving new memory for it. - */ - *tmp = mystrcpy(*tmp, str); - - /* set the option on all threads if setting the option - * for thread 0; otherwise just set that one thread's option - */ - if (cnt[0]->threadnr) - return cnt; - } - return cnt; + char **tmp; + int i; + + i = -1; + + while (cnt[++i]) { + tmp = (char **)((char *)cnt[i] + val_ptr); + + /* mystrcpy assigns the new string value + * including free'ing and reserving new memory for it. + */ + *tmp = mystrcpy(*tmp, str); + + /* set the option on all threads if setting the option + * for thread 0; otherwise just set that one thread's option + */ + if (cnt[0]->threadnr) + return cnt; + } + + return cnt; } @@ -1873,15 +1900,15 @@ struct context **copy_string(struct context **cnt, const char *str, int val_ptr) */ char *mystrcpy(char *to, const char *from) { - /* free the memory used by the to string, if such memory exists, - * and return a pointer to a freshly malloc()'d string with the - * same value as from. - */ + /* free the memory used by the to string, if such memory exists, + * and return a pointer to a freshly malloc()'d string with the + * same value as from. + */ - if (to != NULL) - free(to); + if (to != NULL) + free(to); - return mystrdup(from); + return mystrdup(from); } @@ -1894,54 +1921,56 @@ char *mystrcpy(char *to, const char *from) */ char *mystrdup(const char *from) { - char *tmp; - size_t stringlength; - - if (from == NULL || !strlen(from)) { - tmp = NULL; - } else { - stringlength = strlen(from); - stringlength = (stringlength < PATH_MAX ? stringlength : PATH_MAX); - tmp = (char *)mymalloc(stringlength + 1); - strncpy(tmp, from, stringlength); - - /* We must ensure the string always has a NULL terminator. - * This necessary because strncpy will not append a NULL terminator - * if the original string is greater than stringlength. - */ - tmp += stringlength; - *tmp = '\0'; - tmp -= stringlength; - } - return tmp; + char *tmp; + size_t stringlength; + + if (from == NULL || !strlen(from)) { + tmp = NULL; + } else { + stringlength = strlen(from); + stringlength = (stringlength < PATH_MAX ? stringlength : PATH_MAX); + tmp = (char *)mymalloc(stringlength + 1); + strncpy(tmp, from, stringlength); + + /* We must ensure the string always has a NULL terminator. + * This necessary because strncpy will not append a NULL terminator + * if the original string is greater than stringlength. + */ + tmp += stringlength; + *tmp = '\0'; + tmp -= stringlength; + } + + return tmp; } const char *config_type(config_param *configparam) { - if (configparam->copy == copy_string) - return "string"; - if (configparam->copy == copy_int) - return "int"; - if (configparam->copy == copy_short) - return "short"; - if (configparam->copy == copy_bool) - return "bool"; - return "unknown"; + if (configparam->copy == copy_string) + return "string"; + if (configparam->copy == copy_int) + return "int"; + if (configparam->copy == copy_short) + return "short"; + if (configparam->copy == copy_bool) + return "bool"; + + return "unknown"; } static const char *print_bool(struct context **cnt, char **str ATTRIBUTE_UNUSED, int parm, unsigned short int threadnr) { - int val = config_params[parm].conf_value; + int val = config_params[parm].conf_value; - if (threadnr && - *(int*)((char *)cnt[threadnr] + val) == *(int*)((char *)cnt[0] + val)) - return NULL; + if (threadnr && + *(int*)((char *)cnt[threadnr] + val) == *(int*)((char *)cnt[0] + val)) + return NULL; - if (*(int*)((char *)cnt[threadnr] + val)) - return "on"; - else - return "off"; + if (*(int*)((char *)cnt[threadnr] + val)) + return "on"; + else + return "off"; } /* print_string returns a pointer to a string containing the value of the config option @@ -1954,66 +1983,70 @@ static const char *print_string(struct context **cnt, char **str ATTRIBUTE_UNUSED, int parm, unsigned short int threadnr) { - int val = config_params[parm].conf_value; - const char **cptr0, **cptr1; - - /* strcmp does not like NULL so we have to check for this also */ - cptr0 = (const char **)((char *)cnt[0] + val); - cptr1 = (const char **)((char *)cnt[threadnr] + val); - if ((threadnr) && (*cptr0 != NULL) && (*cptr1 != NULL) && (!strcmp(*cptr0, *cptr1))) - return NULL; - - return *cptr1; + int val = config_params[parm].conf_value; + const char **cptr0, **cptr1; + + /* strcmp does not like NULL so we have to check for this also */ + cptr0 = (const char **)((char *)cnt[0] + val); + cptr1 = (const char **)((char *)cnt[threadnr] + val); + + if ((threadnr) && (*cptr0 != NULL) && (*cptr1 != NULL) && (!strcmp(*cptr0, *cptr1))) + return NULL; + + return *cptr1; } static const char *print_int(struct context **cnt, char **str ATTRIBUTE_UNUSED, int parm, unsigned short int threadnr) { - static char retval[20]; - int val = config_params[parm].conf_value; + static char retval[20]; + int val = config_params[parm].conf_value; - if (threadnr && - *(int*)((char *)cnt[threadnr] + val) == *(int*)((char *)cnt[0] + val)) - return NULL; + if (threadnr && + *(int*)((char *)cnt[threadnr] + val) == *(int*)((char *)cnt[0] + val)) + return NULL; - sprintf(retval, "%d", *(int*)((char *)cnt[threadnr] + val)); + sprintf(retval, "%d", *(int*)((char *)cnt[threadnr] + val)); - return retval; + return retval; } static const char *print_short(struct context **cnt, char **str ATTRIBUTE_UNUSED, int parm, unsigned short int threadnr) { - static char retval[20]; - int val = config_params[parm].conf_value; + static char retval[20]; + int val = config_params[parm].conf_value; + + if (threadnr && + *(short int*)((char *)cnt[threadnr] + val) == *(short int*)((char *)cnt[0] + val)) + return NULL; - if (threadnr && - *(short int*)((char *)cnt[threadnr] + val) == *(short int*)((char *)cnt[0] + val)) - return NULL; - sprintf(retval, "%d", *(short int*)((char *)cnt[threadnr] + val)); + sprintf(retval, "%d", *(short int*)((char *)cnt[threadnr] + val)); - return retval; + return retval; } static const char *print_thread(struct context **cnt, char **str, int parm ATTRIBUTE_UNUSED, unsigned short int threadnr) { - char *retval; - unsigned short int i = 0; + char *retval; + unsigned short int i = 0; + + if (!str || threadnr) + return NULL; + + retval = mymalloc(1); + retval[0] = 0; - if (!str || threadnr) - return NULL; + while (cnt[++i]) { + retval = myrealloc(retval, strlen(retval) + strlen(cnt[i]->conf_filename)+10, "print_thread"); + sprintf(retval + strlen(retval), "thread %s\n", cnt[i]->conf_filename); + } - retval = mymalloc(1); - retval[0] = 0; - while (cnt[++i]) { - retval = myrealloc(retval, strlen(retval) + strlen(cnt[i]->conf_filename)+10, "print_thread"); - sprintf(retval + strlen(retval), "thread %s\n", cnt[i]->conf_filename); - } - *str = retval; + *str = retval; - return NULL; + return NULL; } /* config_thread() is called during initial config file loading each time Motion @@ -2029,73 +2062,74 @@ static const char *print_thread(struct context **cnt, char **str, static struct context **config_thread(struct context **cnt, const char *str, int val ATTRIBUTE_UNUSED) { - int i; - FILE *fp; - - if (cnt[0]->threadnr) - return cnt; - - fp = fopen(str, "r"); - if (!fp) { - motion_log(LOG_ERR, 1, "%s: Thread config file %s not found", - __FUNCTION__, str); - return cnt; - } - - /* Find the current number of threads defined. */ - i=-1; - while (cnt[++i]); - - /* Make space for the threads + the terminating NULL pointer - * in the array of pointers to context structures - * First thread is 0 so the number of threads is i+1 - * plus an extra for the NULL pointer. This gives i+2 - */ - cnt = myrealloc(cnt, sizeof(struct context *)*(i+2), "config_thread"); - - /* Now malloc space for an additional context structure for thread nr. i */ - cnt[i] = mymalloc(sizeof(struct context)); - - /* And make this an exact clone of the context structure for thread 0 */ - memcpy(cnt[i], cnt[0], sizeof(struct context)); - - /* All the integers are copies of the actual value. - * The strings are all pointers to strings so we need to create - * unique malloc'ed space for all the strings that are not NULL and - * change the string pointers to point to the new strings. - * malloc_strings takes care of this. - */ - malloc_strings(cnt[i]); - - /* Mark the end if the array of pointers to context structures */ - cnt[i+1] = NULL; - - /* process the thread's config file and notify user on console */ - strcpy(cnt[i]->conf_filename, str); - motion_log(LOG_INFO, 0, "%s: Processing config file %s", __FUNCTION__, str); - conf_process(cnt+i, fp); - - /* Finally we close the thread config file */ - fclose(fp); - - return cnt; + int i; + FILE *fp; + + if (cnt[0]->threadnr) + return cnt; + + fp = fopen(str, "r"); + + if (!fp) { + motion_log(LOG_ERR, 1, "%s: Thread config file %s not found", + __FUNCTION__, str); + return cnt; + } + + /* Find the current number of threads defined. */ + i = -1; + + while (cnt[++i]); + + /* Make space for the threads + the terminating NULL pointer + * in the array of pointers to context structures + * First thread is 0 so the number of threads is i+1 + * plus an extra for the NULL pointer. This gives i+2 + */ + cnt = myrealloc(cnt, sizeof(struct context *)*(i+2), "config_thread"); + + /* Now malloc space for an additional context structure for thread nr. i */ + cnt[i] = mymalloc(sizeof(struct context)); + + /* And make this an exact clone of the context structure for thread 0 */ + memcpy(cnt[i], cnt[0], sizeof(struct context)); + + /* All the integers are copies of the actual value. + * The strings are all pointers to strings so we need to create + * unique malloc'ed space for all the strings that are not NULL and + * change the string pointers to point to the new strings. + * malloc_strings takes care of this. + */ + malloc_strings(cnt[i]); + + /* Mark the end if the array of pointers to context structures */ + cnt[i+1] = NULL; + + /* process the thread's config file and notify user on console */ + strcpy(cnt[i]->conf_filename, str); + motion_log(LOG_INFO, 0, "%s: Processing config file %s", __FUNCTION__, str); + conf_process(cnt+i, fp); + + /* Finally we close the thread config file */ + fclose(fp); + + return cnt; } static void usage () { - printf("motion Version "VERSION", Copyright 2000-2005 Jeroen Vreeken/Folkert van Heusden/Kenneth Lavrsen\n"); - printf("\nusage:\tmotion [options]\n"); - printf("\n\n"); - printf("Possible options:\n\n"); - printf("-n\t\t\tRun in non-daemon mode.\n"); - printf("-s\t\t\tRun in setup mode.\n"); - printf("-c config\t\tFull path and filename of config file.\n"); - printf("-d level\t\tDebug mode.\n"); - printf("-p process_id_file\tFull path and filename of process id file (pid file).\n"); - printf("-h\t\t\tShow this screen.\n"); - printf("\n"); - printf("Motion is configured using a config file only. If none is supplied,\n"); - printf("it will read motion.conf from current directory, ~/.motion or %s.\n", sysconfdir); - printf("\n"); - + printf("motion Version "VERSION", Copyright 2000-2005 Jeroen Vreeken/Folkert van Heusden/Kenneth Lavrsen\n"); + printf("\nusage:\tmotion [options]\n"); + printf("\n\n"); + printf("Possible options:\n\n"); + printf("-n\t\t\tRun in non-daemon mode.\n"); + printf("-s\t\t\tRun in setup mode.\n"); + printf("-c config\t\tFull path and filename of config file.\n"); + printf("-d level\t\tDebug mode.\n"); + printf("-p process_id_file\tFull path and filename of process id file (pid file).\n"); + printf("-h\t\t\tShow this screen.\n"); + printf("\n"); + printf("Motion is configured using a config file only. If none is supplied,\n"); + printf("it will read motion.conf from current directory, ~/.motion or %s.\n", sysconfdir); + printf("\n"); } diff --git a/draw.c b/draw.c index 8a5db19..cf96c7a 100644 --- a/draw.c +++ b/draw.c @@ -1,11 +1,11 @@ /* - * draw.c + * draw.c * - * Routines for drawing text on images + * Routines for drawing text on images * - * Copyright 2000, Jeroen Vreeken - * This program is published under the GNU public license version 2 - * See also the file 'COPYING' + * Copyright 2000, Jeroen Vreeken + * This program is published under the GNU public license version 2 + * See also the file 'COPYING' * */ @@ -20,1056 +20,1056 @@ unsigned char *small_char_arr_ptr[ASCII_MAX]; unsigned char *big_char_arr_ptr[ASCII_MAX]; struct draw_char { - unsigned char ascii; - unsigned char pix[8][7]; + unsigned char ascii; + unsigned char pix[8][7]; }; struct big_char { - unsigned char ascii; - unsigned char pix[16][14]; + unsigned char ascii; + unsigned char pix[16][14]; }; struct draw_char draw_table[]= { - { - ' ', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0} - } - }, - { - '0', - { - {0,0,1,1,1,0,0}, - {0,1,2,2,2,1,0}, - {1,2,1,1,2,2,1}, - {1,2,1,2,1,2,1}, - {1,2,1,2,1,2,1}, - {1,2,2,1,1,2,1}, - {0,1,2,2,2,1,0}, - {0,0,1,1,1,0,0} - } - }, - { - '1', - { - {0,0,0,1,0,0,0}, - {0,0,1,2,1,0,0}, - {0,1,2,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,1,2,2,2,1,0}, - {0,0,1,1,1,0,0} - } - }, - { - '2', - { - {0,0,1,1,1,0,0}, - {0,1,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {0,1,1,2,2,1,0}, - {0,1,2,1,1,0,0}, - {1,2,1,1,1,1,0}, - {1,2,2,2,2,2,1}, - {0,1,1,1,1,1,0} - } - }, - { - '3', - { - {0,0,1,1,1,0,0}, - {0,1,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {0,1,1,2,2,1,0}, - {0,1,0,1,1,2,1}, - {1,2,1,1,1,2,1}, - {0,1,2,2,2,1,0}, - {0,0,1,1,1,0,0} - } - }, - { - '4', - { - {0,0,0,0,1,0,0}, - {0,0,0,1,2,1,0}, - {0,0,1,2,2,1,0}, - {0,1,2,1,2,1,0}, - {1,2,2,2,2,2,1}, - {0,1,1,1,2,1,0}, - {0,0,0,1,2,1,0}, - {0,0,0,0,1,0,0} - } - }, - { - '5', - { - {0,1,1,1,1,1,0}, - {1,2,2,2,2,2,1}, - {1,2,1,1,1,1,0}, - {1,2,2,2,2,1,0}, - {0,1,1,1,1,2,0}, - {0,1,1,1,1,2,0}, - {1,2,2,2,2,1,0}, - {0,1,1,1,1,0,0} - } - }, - { - '6', - { - {0,0,1,1,1,1,0}, - {0,1,2,2,2,2,1}, - {1,2,1,1,1,1,0}, - {1,2,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {1,2,1,1,1,2,1}, - {0,1,2,2,2,1,0}, - {0,0,1,1,1,0,0} - } - }, - { - '7', - { - {0,1,1,1,1,1,0}, - {1,2,2,2,2,2,1}, - {0,1,1,1,1,2,1}, - {0,0,0,1,2,1,0}, - {0,0,1,2,1,0,0}, - {0,1,2,1,0,0,0}, - {0,1,2,1,0,0,0}, - {0,0,1,0,0,0,0} - } - }, - { - '8', - { - {0,0,1,1,1,0,0}, - {0,1,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {0,1,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {1,2,1,1,1,2,1}, - {0,1,2,2,2,1,0}, - {0,0,1,1,1,0,0} - } - }, - { - '9', - { - {0,0,1,1,1,0,0}, - {0,1,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {0,1,2,2,2,2,1}, - {0,1,1,1,1,2,1}, - {1,2,1,1,1,2,1}, - {0,1,2,2,2,1,0}, - {0,0,1,1,1,0,0} - } - }, - { - '"', - { - {0,0,1,0,1,0,0}, - {0,1,2,1,2,1,0}, - {0,1,2,1,2,1,0}, - {0,0,1,0,1,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0} - } - }, - { - '/', - { - {0,0,0,0,1,0,0}, - {0,0,0,1,2,1,0}, - {0,0,0,1,2,1,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,1,2,1,0,0,0}, - {0,1,2,1,0,0,0}, - {0,0,1,0,0,0,0} - } - }, - { - '(', - { - {0,0,0,1,0,0,0}, - {0,0,1,2,1,0,0}, - {0,1,2,1,0,0,0}, - {0,1,2,1,0,0,0}, - {0,1,2,1,0,0,0}, - {0,1,2,1,0,0,0}, - {0,0,1,2,1,0,0}, - {0,0,0,1,0,0,0} - } - }, - { - ')', - { - {0,0,0,1,0,0,0}, - {0,0,1,2,1,0,0}, - {0,0,0,1,2,1,0}, - {0,0,0,1,2,1,0}, - {0,0,0,1,2,1,0}, - {0,0,0,1,2,1,0}, - {0,0,1,2,1,0,0}, - {0,0,0,1,0,0,0} - } - }, - { - '@', - { - {0,0,1,1,1,0,0}, - {0,1,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {1,2,1,2,2,2,1}, - {1,2,1,2,2,2,1}, - {1,2,1,1,1,1,0}, - {0,1,2,2,2,1,0}, - {0,0,1,1,1,0,0} - } - }, - { - '~', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,1,0,0,0,0}, - {0,1,2,1,0,1,0}, - {1,2,1,2,1,2,1}, - {0,1,0,1,2,1,0}, - {0,0,0,0,1,0,0}, - {0,0,0,0,0,0,0} - } - }, - { - '#', - { - {0,0,1,0,1,0,0}, - {0,1,2,1,2,1,0}, - {1,2,2,2,2,2,1}, - {0,1,2,1,2,1,0}, - {0,1,2,1,2,1,0}, - {1,2,2,2,2,2,1}, - {0,1,2,1,2,1,0}, - {0,0,1,0,1,0,0} - } - }, - { - '<', - { - {0,0,0,0,0,1,0}, - {0,0,0,1,1,2,1}, - {0,1,1,2,2,1,0}, - {1,2,2,1,1,0,0}, - {0,1,1,2,2,1,0}, - {0,0,0,1,1,2,1}, - {0,0,0,0,0,1,0}, - {0,0,0,0,0,0,0} - } - }, - { - '>', - { - {0,1,0,0,0,0,0}, - {1,2,1,1,0,0,0}, - {0,1,2,2,1,1,0}, - {0,0,1,1,2,2,1}, - {0,1,2,2,1,1,0}, - {1,2,1,1,0,0,0}, - {0,1,0,0,0,0,0}, - {0,0,0,0,0,0,0} - } - }, - { - '|', - { - {0,0,0,1,0,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,0,1,0,0,0} - } - }, - { - ',', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,1,1,0,0,0}, - {0,1,2,2,1,0,0}, - {0,1,2,2,1,0,0}, - {0,1,2,1,0,0,0}, - {0,0,1,0,0,0,0} - } - }, - { - '.', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,1,1,0,0,0}, - {0,1,2,2,1,0,0}, - {0,1,2,2,1,0,0}, - {0,0,1,1,0,0,0}, - {0,0,0,0,0,0,0} - } - }, - { - ':', - { - {0,0,1,1,0,0,0}, - {0,1,2,2,1,0,0}, - {0,1,2,2,1,0,0}, - {0,0,1,1,0,0,0}, - {0,0,1,1,0,0,0}, - {0,1,2,2,1,0,0}, - {0,1,2,2,1,0,0}, - {0,0,1,1,0,0,0} - } - }, - { - '-', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,1,1,1,0,0}, - {0,1,2,2,2,1,0}, - {0,0,1,1,1,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0} - } - }, - { - '+', - { - {0,0,0,0,0,0,0}, - {0,0,0,1,0,0,0}, - {0,0,1,2,1,0,0}, - {0,1,2,2,2,1,0}, - {0,0,1,2,1,0,0}, - {0,0,0,1,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0} - } - }, - { - '_', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,1,1,1,1,1,0}, - {1,2,2,2,2,2,1}, - {0,1,1,1,1,1,0} - } - }, - { - '\'', - { - {0,0,0,1,0,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,0,1,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0} - } - }, - { - 'a', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,1,1,1,1,0}, - {0,1,2,2,2,2,1}, - {1,2,1,1,1,2,1}, - {1,2,1,1,1,2,1}, - {0,1,2,2,2,2,1}, - {0,0,1,1,1,1,0} - } - }, - { - 'b', - { - {0,1,0,0,0,0,0}, - {1,2,1,0,0,0,0}, - {1,2,1,1,1,0,0}, - {1,2,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {1,2,1,1,1,2,1}, - {1,2,2,2,2,1,0}, - {0,1,1,1,1,0,0} - } - }, - { - 'c', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,1,1,1,1,0}, - {0,1,2,2,2,2,1}, - {1,2,1,1,1,1,0}, - {1,2,1,1,1,1,0}, - {0,1,2,2,2,2,1}, - {0,0,1,1,1,1,0} - } - }, - { - 'd', - { - {0,0,0,0,0,1,0}, - {0,0,0,0,1,2,1}, - {0,0,1,1,1,2,1}, - {0,1,2,2,2,2,1}, - {1,2,1,1,1,2,1}, - {1,2,1,1,1,2,1}, - {0,1,2,2,2,2,1}, - {0,0,1,1,1,1,0} - } - }, - { - 'e', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,1,1,1,0,0}, - {0,1,2,2,2,1,0}, - {1,2,2,1,1,2,1}, - {1,2,1,2,2,1,0}, - {0,1,2,2,2,2,1}, - {0,0,1,1,1,1,0} - } - }, - { - 'f', - { - {0,0,0,0,1,1,0}, - {0,0,0,1,2,2,1}, - {0,0,1,2,1,1,0}, - {0,1,2,2,2,1,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,0,1,0,0,0} - } - }, - { - 'g', - { - {0,0,0,0,0,0,0}, - {0,0,1,1,1,1,0}, - {0,1,2,2,2,2,1}, - {1,2,1,1,1,2,1}, - {0,1,2,2,2,2,1}, - {0,1,1,1,1,2,1}, - {1,2,2,2,2,1,0}, - {0,1,1,1,1,0,0} - } - }, - { - 'h', - { - {0,1,0,0,0,0,0}, - {1,2,1,0,0,0,0}, - {1,2,1,1,1,0,0}, - {1,2,1,2,2,1,0}, - {1,2,2,1,1,2,1}, - {1,2,1,0,1,2,1}, - {1,2,1,0,1,2,1}, - {0,1,0,0,0,1,0} - } - }, - { - 'i', - { - {0,0,0,1,0,0,0}, - {0,0,1,2,1,0,0}, - {0,0,0,1,0,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,1,2,2,2,1,0}, - {0,0,1,1,1,0,0} - } - }, - { - 'j', - { - {0,0,0,1,0,0,0}, - {0,0,1,2,1,0,0}, - {0,0,0,1,0,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,1,1,2,1,0,0}, - {1,2,2,1,0,0,0}, - {0,1,1,0,0,0,0} - } - }, - { - 'k', - { - {0,1,0,0,0,0,0}, - {1,2,1,0,0,0,0}, - {1,2,1,0,1,0,0}, - {1,2,1,1,2,1,0}, - {1,2,1,2,1,0,0}, - {1,2,2,1,2,1,0}, - {1,2,1,0,1,2,1}, - {0,1,0,0,0,1,0} - } - }, - { - 'l', - { - {0,0,1,1,0,0,0}, - {0,1,2,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,0,1,2,1,0}, - {0,0,0,0,1,0,0} - } - }, - { - 'm', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,1,1,0,1,0,0}, - {1,2,2,1,2,1,0}, - {1,2,1,2,1,2,1}, - {1,2,1,2,1,2,1}, - {1,2,1,2,1,2,1}, - {0,1,0,1,0,1,0} - } - }, - { - 'n', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,1,0,1,1,0,0}, - {1,2,1,2,2,1,0}, - {1,2,2,1,1,2,1}, - {1,2,1,0,1,2,1}, - {1,2,1,0,1,2,1}, - {0,1,0,0,0,1,0} - } - }, - { - 'o', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,1,1,1,0,0}, - {0,1,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {1,2,1,1,1,2,1}, - {0,1,2,2,2,1,0}, - {0,0,1,1,1,0,0} - } - }, - { - 'p', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,1,1,1,1,0,0}, - {1,2,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {1,2,2,2,2,1,0}, - {1,2,1,1,1,0,0}, - {1,2,1,0,0,0,0}, - } - }, - { - 'q', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,1,1,1,1,0}, - {0,1,2,2,2,2,1}, - {1,2,1,1,1,2,1}, - {0,1,2,2,2,2,1}, - {0,0,1,1,1,2,1}, - {0,0,0,0,1,2,1} - } - }, - { - 'r', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,1,0,1,1,0,0}, - {1,2,1,2,2,1,0}, - {1,2,2,1,1,2,1}, - {1,2,1,0,0,1,0}, - {1,2,1,0,0,0,0}, - {0,1,0,0,0,0,0} - } - }, - { - 's', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,0,1,1,1,1,0}, - {0,1,2,2,2,2,1}, - {1,2,2,2,1,1,0}, - {0,1,1,2,2,2,1}, - {1,2,2,2,2,1,0}, - {0,1,1,1,1,0,0} - } - }, - { - 't', - { - {0,0,0,1,0,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,1,2,2,2,1,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,0,1,2,1,0}, - {0,0,0,0,1,0,0} - } - }, - { - 'u', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,1,0,0,0,1,0}, - {1,2,1,0,1,2,1}, - {1,2,1,0,1,2,1}, - {1,2,1,1,2,2,1}, - {0,1,2,2,1,2,1}, - {0,0,1,1,0,1,0} - } - }, - { - 'v', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,1,0,0,0,1,0}, - {1,2,1,0,1,2,1}, - {1,2,1,0,1,2,1}, - {0,1,2,1,2,1,0}, - {0,0,1,2,1,0,0}, - {0,0,0,1,0,0,0} - } - }, - { - 'w', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,1,0,0,0,1,0}, - {1,2,1,0,1,2,1}, - {1,2,1,1,1,2,1}, - {1,2,1,2,1,2,1}, - {0,1,2,1,2,1,0}, - {0,0,1,0,1,0,0} - } - }, - { - 'x', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,1,0,0,1,0,0}, - {1,2,1,1,2,1,0}, - {0,1,2,2,1,0,0}, - {0,1,2,2,1,0,0}, - {1,2,1,1,2,1,0}, - {0,1,0,0,1,0,0} - } - }, - { - 'y', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,1,0,0,0,1,0}, - {1,2,1,0,1,2,1}, - {0,1,2,1,2,1,0}, - {0,0,1,2,1,0,0}, - {0,1,2,1,0,0,0}, - {1,2,1,0,0,0,0} - } - }, - { - 'z', - { - {0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0}, - {0,1,1,1,1,0,0}, - {1,2,2,2,2,1,0}, - {0,1,1,2,1,0,0}, - {0,1,2,1,1,0,0}, - {1,2,2,2,2,1,0}, - {0,1,1,1,1,0,0} - } - }, - { - 'A', - { - {0,0,1,1,1,0,0}, - {0,1,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {1,2,1,1,1,2,1}, - {1,2,2,2,2,2,1}, - {1,2,1,1,1,2,1}, - {1,2,1,0,1,2,1}, - {0,1,0,0,0,1,0} - } - }, - { - 'B', - { - {0,1,1,1,1,0,0}, - {1,2,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {1,2,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {1,2,1,1,1,2,1}, - {1,2,2,2,2,1,0}, - {0,1,1,1,1,0,0} - } - }, - { - 'C', - { - {0,0,1,1,1,0,0}, - {0,1,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {1,2,1,0,0,1,0}, - {1,2,1,0,0,1,0}, - {1,2,1,1,1,2,1}, - {0,1,2,2,2,1,0}, - {0,0,1,1,1,0,0} - } - }, - { - 'D', - { - {0,1,1,1,1,0,0}, - {1,2,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {1,2,1,0,1,2,1}, - {1,2,1,0,1,2,1}, - {1,2,1,1,1,2,1}, - {1,2,2,2,2,1,0}, - {0,1,1,1,1,0,0} - } - }, - { - 'E', - { - {0,1,1,1,1,1,0}, - {1,2,2,2,2,2,1}, - {1,2,1,1,1,1,0}, - {1,2,2,2,2,1,0}, - {1,2,1,1,1,0,0}, - {1,2,1,1,1,1,0}, - {1,2,2,2,2,2,1}, - {0,1,1,1,1,1,0} - } - }, - { - 'F', - { - {0,1,1,1,1,1,0}, - {1,2,2,2,2,2,1}, - {1,2,1,1,1,1,0}, - {1,2,2,2,2,1,0}, - {1,2,1,1,1,0,0}, - {1,2,1,0,0,0,0}, - {1,2,1,0,0,0,0}, - {0,1,0,0,0,0,0} - } - }, - { - 'G', - { - {0,0,1,1,1,0,0}, - {0,1,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {1,2,1,1,1,1,0}, - {1,2,1,2,2,2,1}, - {1,2,1,1,1,2,1}, - {0,1,2,2,2,1,0}, - {0,0,1,1,1,0,0} - } - }, - { - 'H', - { - {0,1,0,0,0,1,0}, - {1,2,1,0,1,2,1}, - {1,2,1,1,1,2,1}, - {1,2,2,2,2,2,1}, - {1,2,1,1,1,2,1}, - {1,2,1,0,1,2,1}, - {1,2,1,0,1,2,1}, - {0,1,0,0,0,1,0} - } - }, - { - 'I', - { - {0,0,1,1,1,0,0}, - {0,1,2,2,2,1,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,1,2,2,2,1,0}, - {0,0,1,1,1,0,0} - } - }, - { - 'J', - { - {0,0,1,1,1,1,0}, - {0,1,2,2,2,2,1}, - {0,0,1,1,1,2,1}, - {0,0,0,0,1,2,1}, - {0,1,0,0,1,2,1}, - {1,2,1,1,1,2,1}, - {0,1,2,2,2,1,0}, - {0,0,1,1,1,0,0} - } - }, - { - 'K', - { - {0,1,0,0,0,1,0}, - {1,2,1,0,1,2,1}, - {1,2,1,1,2,1,0}, - {1,2,1,2,1,0,0}, - {1,2,2,2,1,0,0}, - {1,2,1,1,2,1,0}, - {1,2,1,0,1,2,1}, - {0,1,0,0,0,1,0} - } - }, - { - 'L', - { - {0,1,0,0,0,0,0}, - {1,2,1,0,0,0,0}, - {1,2,1,0,0,0,0}, - {1,2,1,0,0,0,0}, - {1,2,1,0,0,0,0}, - {1,2,1,1,1,0,0}, - {1,2,2,2,2,1,0}, - {0,1,1,1,1,0,0} - } - }, - { - 'M', - { - {0,1,1,0,1,1,0}, - {1,2,2,1,2,2,1}, - {1,2,1,2,1,2,1}, - {1,2,1,1,1,2,}, - {1,2,1,0,1,2,1}, - {1,2,1,0,1,2,1}, - {1,2,1,0,1,2,1}, - {0,1,0,0,0,1,0} - } - }, - { - 'N', - { - {0,1,0,0,0,1,0}, - {1,2,1,0,1,2,1}, - {1,2,2,1,1,2,1}, - {1,2,1,2,1,2,1}, - {1,2,1,1,2,2,1}, - {1,2,1,0,1,2,1}, - {1,2,1,0,1,2,1}, - {0,1,0,0,0,1,0} - } - }, - { - 'O', - { - {0,0,1,1,1,0,0}, - {0,1,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {1,2,1,0,1,2,1}, - {1,2,1,0,1,2,1}, - {1,2,1,1,1,2,1}, - {0,1,2,2,2,1,0}, - {0,0,1,1,1,0,0} - } - }, - { - 'P', - { - {0,1,1,1,1,0,0}, - {1,2,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {1,2,2,2,2,1,0}, - {1,2,1,1,1,0,0}, - {1,2,1,0,0,0,0}, - {1,2,1,0,0,0,0}, - {0,1,0,0,0,0,0} - } - }, - { - 'Q', - { - {0,0,1,1,1,0,0}, - {0,1,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {1,2,1,1,1,2,1}, - {1,2,1,2,1,2,1}, - {1,2,1,1,2,1,0}, - {0,1,2,2,1,2,1}, - {0,0,1,1,0,1,0} - } - }, - { - 'R', - { - {0,1,1,1,1,0,0}, - {1,2,2,2,2,1,0}, - {1,2,1,1,1,2,1}, - {1,2,2,2,2,1,0}, - {1,2,1,2,1,0,0}, - {1,2,1,1,2,1,0}, - {1,2,1,0,1,2,1}, - {0,1,0,0,0,1,0} - } - }, - { - 'S', - { - {0,0,1,1,1,1,0}, - {0,1,2,2,2,2,1}, - {1,2,1,1,1,1,0}, - {0,1,2,2,2,1,0}, - {0,0,1,1,1,2,1}, - {0,1,1,1,1,2,1}, - {1,2,2,2,2,1,0}, - {0,1,1,1,1,0,0} - } - }, - { - 'T', - { - {0,1,1,1,1,1,0}, - {1,2,2,2,2,2,1}, - {0,1,1,2,1,1,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,0,1,0,0,0} - } - }, - { - 'U', - { - {0,1,0,0,0,1,0}, - {1,2,1,0,1,2,1}, - {1,2,1,0,1,2,1}, - {1,2,1,0,1,2,1}, - {1,2,1,0,1,2,1}, - {1,2,1,1,1,2,1}, - {0,1,2,2,2,2,1}, - {0,0,1,1,1,1,0} - } - }, - { - 'V', - { - {0,1,0,0,0,1,0}, - {1,2,1,0,1,2,1}, - {1,2,1,0,1,2,1}, - {1,2,1,0,1,2,1}, - {1,2,1,0,1,2,1}, - {0,1,2,1,2,1,0}, - {0,0,1,2,1,0,0}, - {0,0,0,1,0,0,0} - } - }, - { - 'W', - { - {0,1,0,0,0,1,0}, - {1,2,1,0,1,2,1}, - {1,2,1,0,1,2,1}, - {1,2,1,1,1,2,1}, - {1,2,1,2,1,2,1}, - {1,2,1,2,1,2,1}, - {0,1,2,1,2,1,0}, - {0,0,1,0,1,0,0} - } - }, - { - 'X', - { - {0,1,0,0,0,1,0}, - {1,2,1,0,1,2,1}, - {0,1,2,1,2,1,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,1,2,1,2,1,0}, - {1,2,1,0,1,2,1}, - {0,1,0,0,0,1,0} - } - }, - { - 'Y', - { - {0,1,0,0,0,1,0}, - {1,2,1,0,1,2,1}, - {0,1,2,1,2,1,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,1,2,1,0,0}, - {0,0,0,1,0,0,0} - } - }, - { - 'Z', - { - {0,1,1,1,1,1,0}, - {1,2,2,2,2,2,1}, - {0,1,1,1,2,1,0}, - {0,0,1,2,1,0,0}, - {0,1,2,1,0,0,0}, - {1,2,1,1,1,1,0}, - {1,2,2,2,2,2,1}, - {0,1,1,1,1,1,0} - } - } + { + ' ', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0} + } + }, + { + '0', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,2,2,1}, + {1,2,1,2,1,2,1}, + {1,2,1,2,1,2,1}, + {1,2,2,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + '1', + { + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,1,2,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + '2', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {0,1,1,2,2,1,0}, + {0,1,2,1,1,0,0}, + {1,2,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {0,1,1,1,1,1,0} + } + }, + { + '3', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {0,1,1,2,2,1,0}, + {0,1,0,1,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + '4', + { + {0,0,0,0,1,0,0}, + {0,0,0,1,2,1,0}, + {0,0,1,2,2,1,0}, + {0,1,2,1,2,1,0}, + {1,2,2,2,2,2,1}, + {0,1,1,1,2,1,0}, + {0,0,0,1,2,1,0}, + {0,0,0,0,1,0,0} + } + }, + { + '5', + { + {0,1,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {1,2,1,1,1,1,0}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,2,0}, + {0,1,1,1,1,2,0}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,0,0} + } + }, + { + '6', + { + {0,0,1,1,1,1,0}, + {0,1,2,2,2,2,1}, + {1,2,1,1,1,1,0}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + '7', + { + {0,1,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {0,1,1,1,1,2,1}, + {0,0,0,1,2,1,0}, + {0,0,1,2,1,0,0}, + {0,1,2,1,0,0,0}, + {0,1,2,1,0,0,0}, + {0,0,1,0,0,0,0} + } + }, + { + '8', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + '9', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,2,1}, + {0,1,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + '"', + { + {0,0,1,0,1,0,0}, + {0,1,2,1,2,1,0}, + {0,1,2,1,2,1,0}, + {0,0,1,0,1,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0} + } + }, + { + '/', + { + {0,0,0,0,1,0,0}, + {0,0,0,1,2,1,0}, + {0,0,0,1,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,1,2,1,0,0,0}, + {0,1,2,1,0,0,0}, + {0,0,1,0,0,0,0} + } + }, + { + '(', + { + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,1,2,1,0,0,0}, + {0,1,2,1,0,0,0}, + {0,1,2,1,0,0,0}, + {0,1,2,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0} + } + }, + { + ')', + { + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,2,1,0}, + {0,0,0,1,2,1,0}, + {0,0,0,1,2,1,0}, + {0,0,0,1,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0} + } + }, + { + '@', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,2,2,2,1}, + {1,2,1,2,2,2,1}, + {1,2,1,1,1,1,0}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + '~', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,0,0,0,0}, + {0,1,2,1,0,1,0}, + {1,2,1,2,1,2,1}, + {0,1,0,1,2,1,0}, + {0,0,0,0,1,0,0}, + {0,0,0,0,0,0,0} + } + }, + { + '#', + { + {0,0,1,0,1,0,0}, + {0,1,2,1,2,1,0}, + {1,2,2,2,2,2,1}, + {0,1,2,1,2,1,0}, + {0,1,2,1,2,1,0}, + {1,2,2,2,2,2,1}, + {0,1,2,1,2,1,0}, + {0,0,1,0,1,0,0} + } + }, + { + '<', + { + {0,0,0,0,0,1,0}, + {0,0,0,1,1,2,1}, + {0,1,1,2,2,1,0}, + {1,2,2,1,1,0,0}, + {0,1,1,2,2,1,0}, + {0,0,0,1,1,2,1}, + {0,0,0,0,0,1,0}, + {0,0,0,0,0,0,0} + } + }, + { + '>', + { + {0,1,0,0,0,0,0}, + {1,2,1,1,0,0,0}, + {0,1,2,2,1,1,0}, + {0,0,1,1,2,2,1}, + {0,1,2,2,1,1,0}, + {1,2,1,1,0,0,0}, + {0,1,0,0,0,0,0}, + {0,0,0,0,0,0,0} + } + }, + { + '|', + { + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0} + } + }, + { + ',', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,1,0,0,0}, + {0,1,2,2,1,0,0}, + {0,1,2,2,1,0,0}, + {0,1,2,1,0,0,0}, + {0,0,1,0,0,0,0} + } + }, + { + '.', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,1,0,0,0}, + {0,1,2,2,1,0,0}, + {0,1,2,2,1,0,0}, + {0,0,1,1,0,0,0}, + {0,0,0,0,0,0,0} + } + }, + { + ':', + { + {0,0,1,1,0,0,0}, + {0,1,2,2,1,0,0}, + {0,1,2,2,1,0,0}, + {0,0,1,1,0,0,0}, + {0,0,1,1,0,0,0}, + {0,1,2,2,1,0,0}, + {0,1,2,2,1,0,0}, + {0,0,1,1,0,0,0} + } + }, + { + '-', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0} + } + }, + { + '+', + { + {0,0,0,0,0,0,0}, + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,1,2,2,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0} + } + }, + { + '_', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {0,1,1,1,1,1,0} + } + }, + { + '\'', + { + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0} + } + }, + { + 'a', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,1,1,1,0}, + {0,1,2,2,2,2,1}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,2,1}, + {0,0,1,1,1,1,0} + } + }, + { + 'b', + { + {0,1,0,0,0,0,0}, + {1,2,1,0,0,0,0}, + {1,2,1,1,1,0,0}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,0,0} + } + }, + { + 'c', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,1,1,1,0}, + {0,1,2,2,2,2,1}, + {1,2,1,1,1,1,0}, + {1,2,1,1,1,1,0}, + {0,1,2,2,2,2,1}, + {0,0,1,1,1,1,0} + } + }, + { + 'd', + { + {0,0,0,0,0,1,0}, + {0,0,0,0,1,2,1}, + {0,0,1,1,1,2,1}, + {0,1,2,2,2,2,1}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,2,1}, + {0,0,1,1,1,1,0} + } + }, + { + 'e', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,2,1,1,2,1}, + {1,2,1,2,2,1,0}, + {0,1,2,2,2,2,1}, + {0,0,1,1,1,1,0} + } + }, + { + 'f', + { + {0,0,0,0,1,1,0}, + {0,0,0,1,2,2,1}, + {0,0,1,2,1,1,0}, + {0,1,2,2,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0} + } + }, + { + 'g', + { + {0,0,0,0,0,0,0}, + {0,0,1,1,1,1,0}, + {0,1,2,2,2,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,2,1}, + {0,1,1,1,1,2,1}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,0,0} + } + }, + { + 'h', + { + {0,1,0,0,0,0,0}, + {1,2,1,0,0,0,0}, + {1,2,1,1,1,0,0}, + {1,2,1,2,2,1,0}, + {1,2,2,1,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'i', + { + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + 'j', + { + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,1,1,2,1,0,0}, + {1,2,2,1,0,0,0}, + {0,1,1,0,0,0,0} + } + }, + { + 'k', + { + {0,1,0,0,0,0,0}, + {1,2,1,0,0,0,0}, + {1,2,1,0,1,0,0}, + {1,2,1,1,2,1,0}, + {1,2,1,2,1,0,0}, + {1,2,2,1,2,1,0}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'l', + { + {0,0,1,1,0,0,0}, + {0,1,2,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,2,1,0}, + {0,0,0,0,1,0,0} + } + }, + { + 'm', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,1,0,1,0,0}, + {1,2,2,1,2,1,0}, + {1,2,1,2,1,2,1}, + {1,2,1,2,1,2,1}, + {1,2,1,2,1,2,1}, + {0,1,0,1,0,1,0} + } + }, + { + 'n', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,0,1,1,0,0}, + {1,2,1,2,2,1,0}, + {1,2,2,1,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'o', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + 'p', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,1,1,1,0,0}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,0,0}, + {1,2,1,0,0,0,0}, + } + }, + { + 'q', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,1,1,1,0}, + {0,1,2,2,2,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,2,1}, + {0,0,1,1,1,2,1}, + {0,0,0,0,1,2,1} + } + }, + { + 'r', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,0,1,1,0,0}, + {1,2,1,2,2,1,0}, + {1,2,2,1,1,2,1}, + {1,2,1,0,0,1,0}, + {1,2,1,0,0,0,0}, + {0,1,0,0,0,0,0} + } + }, + { + 's', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,0,1,1,1,1,0}, + {0,1,2,2,2,2,1}, + {1,2,2,2,1,1,0}, + {0,1,1,2,2,2,1}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,0,0} + } + }, + { + 't', + { + {0,0,0,1,0,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,1,2,2,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,2,1,0}, + {0,0,0,0,1,0,0} + } + }, + { + 'u', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,1,2,2,1}, + {0,1,2,2,1,2,1}, + {0,0,1,1,0,1,0} + } + }, + { + 'v', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {0,1,2,1,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0} + } + }, + { + 'w', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {1,2,1,1,1,2,1}, + {1,2,1,2,1,2,1}, + {0,1,2,1,2,1,0}, + {0,0,1,0,1,0,0} + } + }, + { + 'x', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,0,0,1,0,0}, + {1,2,1,1,2,1,0}, + {0,1,2,2,1,0,0}, + {0,1,2,2,1,0,0}, + {1,2,1,1,2,1,0}, + {0,1,0,0,1,0,0} + } + }, + { + 'y', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {0,1,2,1,2,1,0}, + {0,0,1,2,1,0,0}, + {0,1,2,1,0,0,0}, + {1,2,1,0,0,0,0} + } + }, + { + 'z', + { + {0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0}, + {0,1,1,1,1,0,0}, + {1,2,2,2,2,1,0}, + {0,1,1,2,1,0,0}, + {0,1,2,1,1,0,0}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,0,0} + } + }, + { + 'A', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {1,2,2,2,2,2,1}, + {1,2,1,1,1,2,1}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'B', + { + {0,1,1,1,1,0,0}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,0,0} + } + }, + { + 'C', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,0,0,1,0}, + {1,2,1,0,0,1,0}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + 'D', + { + {0,1,1,1,1,0,0}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,1,1,2,1}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,0,0} + } + }, + { + 'E', + { + {0,1,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {1,2,1,1,1,1,0}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,0,0}, + {1,2,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {0,1,1,1,1,1,0} + } + }, + { + 'F', + { + {0,1,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {1,2,1,1,1,1,0}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,0,0}, + {1,2,1,0,0,0,0}, + {1,2,1,0,0,0,0}, + {0,1,0,0,0,0,0} + } + }, + { + 'G', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,1,0}, + {1,2,1,2,2,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + 'H', + { + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {1,2,1,1,1,2,1}, + {1,2,2,2,2,2,1}, + {1,2,1,1,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'I', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + 'J', + { + {0,0,1,1,1,1,0}, + {0,1,2,2,2,2,1}, + {0,0,1,1,1,2,1}, + {0,0,0,0,1,2,1}, + {0,1,0,0,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + 'K', + { + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {1,2,1,1,2,1,0}, + {1,2,1,2,1,0,0}, + {1,2,2,2,1,0,0}, + {1,2,1,1,2,1,0}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'L', + { + {0,1,0,0,0,0,0}, + {1,2,1,0,0,0,0}, + {1,2,1,0,0,0,0}, + {1,2,1,0,0,0,0}, + {1,2,1,0,0,0,0}, + {1,2,1,1,1,0,0}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,0,0} + } + }, + { + 'M', + { + {0,1,1,0,1,1,0}, + {1,2,2,1,2,2,1}, + {1,2,1,2,1,2,1}, + {1,2,1,1,1,2,}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'N', + { + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {1,2,2,1,1,2,1}, + {1,2,1,2,1,2,1}, + {1,2,1,1,2,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'O', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,0,0} + } + }, + { + 'P', + { + {0,1,1,1,1,0,0}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,0,0}, + {1,2,1,0,0,0,0}, + {1,2,1,0,0,0,0}, + {0,1,0,0,0,0,0} + } + }, + { + 'Q', + { + {0,0,1,1,1,0,0}, + {0,1,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,1,1,1,2,1}, + {1,2,1,2,1,2,1}, + {1,2,1,1,2,1,0}, + {0,1,2,2,1,2,1}, + {0,0,1,1,0,1,0} + } + }, + { + 'R', + { + {0,1,1,1,1,0,0}, + {1,2,2,2,2,1,0}, + {1,2,1,1,1,2,1}, + {1,2,2,2,2,1,0}, + {1,2,1,2,1,0,0}, + {1,2,1,1,2,1,0}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'S', + { + {0,0,1,1,1,1,0}, + {0,1,2,2,2,2,1}, + {1,2,1,1,1,1,0}, + {0,1,2,2,2,1,0}, + {0,0,1,1,1,2,1}, + {0,1,1,1,1,2,1}, + {1,2,2,2,2,1,0}, + {0,1,1,1,1,0,0} + } + }, + { + 'T', + { + {0,1,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {0,1,1,2,1,1,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0} + } + }, + { + 'U', + { + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,1,1,2,1}, + {0,1,2,2,2,2,1}, + {0,0,1,1,1,1,0} + } + }, + { + 'V', + { + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {0,1,2,1,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0} + } + }, + { + 'W', + { + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {1,2,1,0,1,2,1}, + {1,2,1,1,1,2,1}, + {1,2,1,2,1,2,1}, + {1,2,1,2,1,2,1}, + {0,1,2,1,2,1,0}, + {0,0,1,0,1,0,0} + } + }, + { + 'X', + { + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {0,1,2,1,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,1,2,1,2,1,0}, + {1,2,1,0,1,2,1}, + {0,1,0,0,0,1,0} + } + }, + { + 'Y', + { + {0,1,0,0,0,1,0}, + {1,2,1,0,1,2,1}, + {0,1,2,1,2,1,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,1,2,1,0,0}, + {0,0,0,1,0,0,0} + } + }, + { + 'Z', + { + {0,1,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {0,1,1,1,2,1,0}, + {0,0,1,2,1,0,0}, + {0,1,2,1,0,0,0}, + {1,2,1,1,1,1,0}, + {1,2,2,2,2,2,1}, + {0,1,1,1,1,1,0} + } + } }; struct big_char big_table[sizeof(draw_table) / sizeof(struct draw_char)]; @@ -1078,103 +1078,116 @@ struct big_char big_table[sizeof(draw_table) / sizeof(struct draw_char)]; static int draw_textn (unsigned char *image, int startx, int starty, int width, const char *text, int len, unsigned short int factor) { - int pos, x, y, line_offset, next_char_offs; - unsigned char *image_ptr, *char_ptr, **char_arr_ptr; + int pos, x, y, line_offset, next_char_offs; + unsigned char *image_ptr, *char_ptr, **char_arr_ptr; - if (startx > width / 2) - startx -= len * (6 * (factor + 1)); + if (startx > width / 2) + startx -= len * (6 * (factor + 1)); - if (startx < 0) - startx = 0; - - if (startx + len * 6 * (factor + 1) >= width) - len = (width-startx-1)/(6*(factor+1)); - - line_offset = width - 7 * (factor + 1); - next_char_offs = width * 8 * (factor + 1) - 6 * (factor + 1); - - image_ptr = image + startx + starty * width; + if (startx < 0) + startx = 0; + + if (startx + len * 6 * (factor + 1) >= width) + len = (width-startx-1)/(6*(factor+1)); + + line_offset = width - 7 * (factor + 1); + next_char_offs = width * 8 * (factor + 1) - 6 * (factor + 1); + + image_ptr = image + startx + starty * width; - char_arr_ptr = factor ? big_char_arr_ptr : small_char_arr_ptr; - - for (pos = 0; pos < len; pos++) { - char_ptr = char_arr_ptr[(int)text[pos]]; - for (y = 8 * (factor + 1); y--; ) { - for (x = 7 * (factor + 1); x--; ) { - switch(*char_ptr) { - case 1: - *image_ptr = 0; - break; - case 2: - *image_ptr = 255; - break; - default: - break; - } - image_ptr++; - char_ptr++; - } - image_ptr += line_offset; - } - image_ptr -= next_char_offs; - } - return 0; + char_arr_ptr = factor ? big_char_arr_ptr : small_char_arr_ptr; + + for (pos = 0; pos < len; pos++) { + char_ptr = char_arr_ptr[(int)text[pos]]; + + for (y = 8 * (factor + 1); y--;) { + for (x = 7 * (factor + 1); x--;) { + switch(*char_ptr) { + case 1: + *image_ptr = 0; + break; + case 2: + *image_ptr = 255; + break; + default: + break; + } + + image_ptr++; + char_ptr++; + } + + image_ptr += line_offset; + } + + image_ptr -= next_char_offs; + } + + return 0; } int draw_text (unsigned char *image, int startx, int starty, int width, const char *text, unsigned short int factor) { - int num_nl = 0; - const char *end, *begin; - const int line_space = (factor + 1) * 9; - - /* Count the number of newlines in "text" so we scroll it up the image */ - end = text; - while ((end = strstr(end, NEWLINE))) { - num_nl++; - end += sizeof(NEWLINE)-1; - } - starty -= line_space * num_nl; - - begin = end = text; - while ((end = strstr(end, NEWLINE))) { - int len = end-begin; - draw_textn(image, startx, starty, width, begin, len, factor); - end += sizeof(NEWLINE)-1; - begin = end; - starty += line_space; - } - draw_textn(image, startx, starty, width, begin, strlen(begin), factor); - return 0; + int num_nl = 0; + const char *end, *begin; + const int line_space = (factor + 1) * 9; + + /* Count the number of newlines in "text" so we scroll it up the image */ + end = text; + + while ((end = strstr(end, NEWLINE))) { + num_nl++; + end += sizeof(NEWLINE)-1; + } + + starty -= line_space * num_nl; + + begin = end = text; + + while ((end = strstr(end, NEWLINE))) { + int len = end-begin; + + draw_textn(image, startx, starty, width, begin, len, factor); + end += sizeof(NEWLINE)-1; + begin = end; + starty += line_space; + } + + draw_textn(image, startx, starty, width, begin, strlen(begin), factor); + + return 0; } int initialize_chars(void) { - unsigned int i, x, y; - size_t draw_table_size; - - draw_table_size = sizeof(draw_table) / sizeof(struct draw_char); + unsigned int i, x, y; + size_t draw_table_size; + + draw_table_size = sizeof(draw_table) / sizeof(struct draw_char); + + /* Fill the structure 'big_table' with double sized characters. */ + for (i = 0; i < draw_table_size; i++) { + big_table[i].ascii = draw_table[i].ascii; + + for(x = 0; x < 14; x++) { + for(y = 0; y < 16; y++) { + big_table[i].pix[y][x] = draw_table[i].pix[y/2][x/2]; + } + } + } - /* Fill the structure 'big_table' with double sized characters. */ - for (i = 0; i < draw_table_size; i++) { - big_table[i].ascii = draw_table[i].ascii; - for(x = 0; x < 14; x++) { - for(y = 0; y < 16; y++) { - big_table[i].pix[y][x] = draw_table[i].pix[y/2][x/2]; - } - } - } + /* first init all char ptr's to a space character */ + for (i = 0; i < ASCII_MAX; i++) { + small_char_arr_ptr[i] = &draw_table[0].pix[0][0]; + big_char_arr_ptr[i] = &big_table[0].pix[0][0]; + } + + /* build [big_]char_arr_ptr table to point to each available ascii */ + for (i = 0; i < draw_table_size; i++) { + small_char_arr_ptr[(int)draw_table[i].ascii] = &draw_table[i].pix[0][0]; + big_char_arr_ptr[(int)draw_table[i].ascii] = &big_table[i].pix[0][0]; + } - /* first init all char ptr's to a space character */ - for (i = 0; i < ASCII_MAX; i++) { - small_char_arr_ptr[i] = &draw_table[0].pix[0][0]; - big_char_arr_ptr[i] = &big_table[0].pix[0][0]; - } - - /* build [big_]char_arr_ptr table to point to each available ascii */ - for (i = 0; i < draw_table_size; i++) { - small_char_arr_ptr[(int)draw_table[i].ascii] = &draw_table[i].pix[0][0]; - big_char_arr_ptr[(int)draw_table[i].ascii] = &big_table[i].pix[0][0]; - } - return 0; + return 0; } diff --git a/event.c b/event.c index 95877b7..473cbcb 100644 --- a/event.c +++ b/event.c @@ -1,23 +1,23 @@ /* - event.c + event.c - Generalised event handling for motion + Generalised event handling for motion - Copyright Jeroen Vreeken, 2002 - This software is distributed under the GNU Public License Version 2 - see also the file 'COPYING'. + Copyright Jeroen Vreeken, 2002 + This software is distributed under the GNU Public License Version 2 + see also the file 'COPYING'. */ -#include "ffmpeg.h" /* must be first to avoid 'shadow' warning */ -#include "picture.h" /* already includes motion.h */ +#include "ffmpeg.h" /* must be first to avoid 'shadow' warning */ +#include "picture.h" /* already includes motion.h */ #include "event.h" #if (!defined(BSD)) #include "video.h" #endif /* - * Various functions (most doing the actual action) + * Various functions (most doing the actual action) */ /* Execute 'command' with 'arg' as its argument. @@ -28,43 +28,43 @@ */ static void exec_command(struct context *cnt, char *command, char *filename, int filetype) { - char stamp[PATH_MAX]; - mystrftime(cnt, stamp, sizeof(stamp), command, &cnt->current_image->timestamp_tm, filename, filetype); - - if (!fork()) { - int i; - - /* Detach from parent */ - setsid(); - - /* - * Close any file descriptor except console because we will - * like to see error messages - */ - for (i = getdtablesize(); i > 2; --i) - close(i); - - execl("/bin/sh", "sh", "-c", stamp, " &", NULL); - - /* if above function succeeds the program never reach here */ - motion_log(LOG_ERR, 1, "%s: Unable to start external command '%s'", __FUNCTION__, stamp); - - exit(1); - } - else if (debug_level >= CAMERA_VERBOSE) - motion_log(-1, 0, "%s: Executing external command '%s'", __FUNCTION__, stamp); + char stamp[PATH_MAX]; + mystrftime(cnt, stamp, sizeof(stamp), command, &cnt->current_image->timestamp_tm, filename, filetype); + + if (!fork()) { + int i; + + /* Detach from parent */ + setsid(); + + /* + * Close any file descriptor except console because we will + * like to see error messages + */ + for (i = getdtablesize(); i > 2; --i) + close(i); + + execl("/bin/sh", "sh", "-c", stamp, " &", NULL); + + /* if above function succeeds the program never reach here */ + motion_log(LOG_ERR, 1, "%s: Unable to start external command '%s'", __FUNCTION__, stamp); + + exit(1); + } else if (debug_level >= CAMERA_VERBOSE) { + motion_log(-1, 0, "%s: Executing external command '%s'", __FUNCTION__, stamp); + } } /* - * Event handlers + * Event handlers */ static void event_newfile(struct context *cnt ATTRIBUTE_UNUSED, int type ATTRIBUTE_UNUSED, unsigned char *dummy ATTRIBUTE_UNUSED, char *filename, void *ftype, struct tm *tm ATTRIBUTE_UNUSED) { - motion_log(-1, 0, "%s: File of type %ld saved to: %s", __FUNCTION__, - (unsigned long)ftype, filename); + motion_log(-1, 0, "%s: File of type %ld saved to: %s", __FUNCTION__, + (unsigned long)ftype, filename); } @@ -74,8 +74,8 @@ static void event_beep(struct context *cnt, int type ATTRIBUTE_UNUSED, void *ftype ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) { - if (!cnt->conf.quiet) - printf("\a"); + if (!cnt->conf.quiet) + printf("\a"); } /* on_picture_save_command handles both on_picture_save and on_movie_start @@ -88,13 +88,13 @@ static void on_picture_save_command(struct context *cnt, int type ATTRIBUTE_UNUSED, unsigned char *dummy ATTRIBUTE_UNUSED, char *filename, void *arg, struct tm *tm ATTRIBUTE_UNUSED) { - int filetype = (unsigned long)arg; + int filetype = (unsigned long)arg; - if ((filetype & FTYPE_IMAGE_ANY) != 0 && cnt->conf.on_picture_save) - exec_command(cnt, cnt->conf.on_picture_save, filename, filetype); + if ((filetype & FTYPE_IMAGE_ANY) != 0 && cnt->conf.on_picture_save) + exec_command(cnt, cnt->conf.on_picture_save, filename, filetype); - if ((filetype & FTYPE_MPEG_ANY) != 0 && cnt->conf.on_movie_start) - exec_command(cnt, cnt->conf.on_movie_start, filename, filetype); + if ((filetype & FTYPE_MPEG_ANY) != 0 && cnt->conf.on_movie_start) + exec_command(cnt, cnt->conf.on_movie_start, filename, filetype); } static void on_motion_detected_command(struct context *cnt, @@ -102,8 +102,8 @@ static void on_motion_detected_command(struct context *cnt, char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) { - if (cnt->conf.on_motion_detected) - exec_command(cnt, cnt->conf.on_motion_detected, NULL, 0); + if (cnt->conf.on_motion_detected) + exec_command(cnt, cnt->conf.on_motion_detected, NULL, 0); } #if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) @@ -112,58 +112,61 @@ static void event_sqlnewfile(struct context *cnt, int type ATTRIBUTE_UNUSED, unsigned char *dummy ATTRIBUTE_UNUSED, char *filename, void *arg, struct tm *tm ATTRIBUTE_UNUSED) { - int sqltype = (unsigned long)arg; - - /* Only log the file types we want */ - if (!(cnt->conf.database_type) || (sqltype & cnt->sql_mask) == 0) - return; - - /* We place the code in a block so we only spend time making space in memory - * for the sqlquery and timestr when we actually need it. - */ - { - char sqlquery[PATH_MAX]; - - mystrftime(cnt, sqlquery, sizeof(sqlquery), cnt->conf.sql_query, &cnt->current_image->timestamp_tm, filename, sqltype); - + int sqltype = (unsigned long)arg; + + /* Only log the file types we want */ + if (!(cnt->conf.database_type) || (sqltype & cnt->sql_mask) == 0) + return; + + /* We place the code in a block so we only spend time making space in memory + * for the sqlquery and timestr when we actually need it. + */ + { + char sqlquery[PATH_MAX]; + + mystrftime(cnt, sqlquery, sizeof(sqlquery), cnt->conf.sql_query, &cnt->current_image->timestamp_tm, filename, sqltype); + #ifdef HAVE_MYSQL - if (!strcmp(cnt->conf.database_type, "mysql")) { - if (mysql_query(cnt->database, sqlquery) != 0){ - int error_code = mysql_errno(cnt->database); - - motion_log(LOG_ERR, 1, "%s: Mysql query failed %s error code %d", - __FUNCTION__, mysql_error(cnt->database), error_code); - /* Try to reconnect ONCE if fails continue and discard this sql query */ - if (error_code >= 2000){ - cnt->database = (MYSQL *) mymalloc(sizeof(MYSQL)); - mysql_init(cnt->database); - if (!mysql_real_connect(cnt->database, cnt->conf.database_host, cnt->conf.database_user, - cnt->conf.database_password, cnt->conf.database_dbname, 0, NULL, 0)) { - motion_log(LOG_ERR, 0, "%s: Cannot reconnect to MySQL database " - "%s on host %s with user %s MySQL error was %s", - __FUNCTION__, cnt->conf.database_dbname, - cnt->conf.database_host, cnt->conf.database_user, - mysql_error(cnt->database)); - - }else mysql_query(cnt->database, sqlquery); - } - } - } + if (!strcmp(cnt->conf.database_type, "mysql")) { + if (mysql_query(cnt->database, sqlquery) != 0) { + int error_code = mysql_errno(cnt->database); + + motion_log(LOG_ERR, 1, "%s: Mysql query failed %s error code %d", + __FUNCTION__, mysql_error(cnt->database), error_code); + /* Try to reconnect ONCE if fails continue and discard this sql query */ + if (error_code >= 2000) { + cnt->database = (MYSQL *) mymalloc(sizeof(MYSQL)); + mysql_init(cnt->database); + + if (!mysql_real_connect(cnt->database, cnt->conf.database_host, cnt->conf.database_user, + cnt->conf.database_password, cnt->conf.database_dbname, 0, NULL, 0)) { + motion_log(LOG_ERR, 0, "%s: Cannot reconnect to MySQL database " + "%s on host %s with user %s MySQL error was %s", + __FUNCTION__, cnt->conf.database_dbname, + cnt->conf.database_host, cnt->conf.database_user, + mysql_error(cnt->database)); + + } else { + mysql_query(cnt->database, sqlquery); + } + } + } + } #endif /* HAVE_MYSQL */ #ifdef HAVE_PGSQL - if (!strcmp(cnt->conf.database_type, "postgresql")) { - PGresult *res; + if (!strcmp(cnt->conf.database_type, "postgresql")) { + PGresult *res; - res = PQexec(cnt->database_pg, sqlquery); + res = PQexec(cnt->database_pg, sqlquery); - if (PQresultStatus(res) != PGRES_COMMAND_OK) { - motion_log(LOG_ERR, 1, "%s: PGSQL query failed", __FUNCTION__); - PQclear(res); - } - } + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + motion_log(LOG_ERR, 1, "%s: PGSQL query failed", __FUNCTION__); + PQclear(res); + } + } #endif /* HAVE_PGSQL */ - } + } } #endif /* defined HAVE_MYSQL || defined HAVE_PGSQL */ @@ -173,8 +176,8 @@ static void on_area_command(struct context *cnt, int type ATTRIBUTE_UNUSED, char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) { - if (cnt->conf.on_area_detected) - exec_command(cnt, cnt->conf.on_area_detected, NULL, 0); + if (cnt->conf.on_area_detected) + exec_command(cnt, cnt->conf.on_area_detected, NULL, 0); } static void on_event_start_command(struct context *cnt, int type ATTRIBUTE_UNUSED, @@ -182,8 +185,8 @@ static void on_event_start_command(struct context *cnt, int type ATTRIBUTE_UNUSE char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) { - if (cnt->conf.on_event_start) - exec_command(cnt, cnt->conf.on_event_start, NULL, 0); + if (cnt->conf.on_event_start) + exec_command(cnt, cnt->conf.on_event_start, NULL, 0); } static void on_event_end_command(struct context *cnt, int type ATTRIBUTE_UNUSED, @@ -191,8 +194,8 @@ static void on_event_end_command(struct context *cnt, int type ATTRIBUTE_UNUSED, char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) { - if (cnt->conf.on_event_end) - exec_command(cnt, cnt->conf.on_event_end, NULL, 0); + if (cnt->conf.on_event_end) + exec_command(cnt, cnt->conf.on_event_end, NULL, 0); } static void event_stop_stream(struct context *cnt, int type ATTRIBUTE_UNUSED, @@ -200,16 +203,16 @@ static void event_stop_stream(struct context *cnt, int type ATTRIBUTE_UNUSED, char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) { - if ((cnt->conf.stream_port) && (cnt->stream.socket != -1)) - stream_stop(cnt); + if ((cnt->conf.stream_port) && (cnt->stream.socket != -1)) + stream_stop(cnt); } static void event_stream_put(struct context *cnt, int type ATTRIBUTE_UNUSED, unsigned char *img, char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) { - if (cnt->conf.stream_port) - stream_put(cnt, img); + if (cnt->conf.stream_port) + stream_put(cnt, img); } #ifndef WITHOUT_V4L @@ -218,229 +221,234 @@ static void event_vid_putpipe(struct context *cnt, int type ATTRIBUTE_UNUSED, unsigned char *img, char *dummy ATTRIBUTE_UNUSED, void *devpipe, struct tm *tm ATTRIBUTE_UNUSED) { - if (*(int *)devpipe >= 0) { - if (vid_putpipe(*(int *)devpipe, img, cnt->imgs.size) == -1) - motion_log(LOG_ERR, 1, "%s: Failed to put image into video pipe", __FUNCTION__); - } + if (*(int *)devpipe >= 0) { + if (vid_putpipe(*(int *)devpipe, img, cnt->imgs.size) == -1) + motion_log(LOG_ERR, 1, "%s: Failed to put image into video pipe", __FUNCTION__); + } } #endif /* BSD */ #endif /* WITHOUT_V4L */ - const char *imageext(struct context *cnt) { - if (cnt->imgs.picture_type == IMAGE_TYPE_PPM) - return "ppm"; - return "jpg"; + if (cnt->imgs.picture_type == IMAGE_TYPE_PPM) + return "ppm"; + + return "jpg"; } static void event_image_detect(struct context *cnt, int type ATTRIBUTE_UNUSED, - unsigned char *newimg, char *dummy1 ATTRIBUTE_UNUSED, - void *dummy2 ATTRIBUTE_UNUSED, struct tm *currenttime_tm) + unsigned char *newimg, char *dummy1 ATTRIBUTE_UNUSED, + void *dummy2 ATTRIBUTE_UNUSED, struct tm *currenttime_tm) { - char fullfilename[PATH_MAX]; - char filename[PATH_MAX]; - - if (cnt->new_img & NEWIMG_ON) { - const char *imagepath; - - /* conf.imagepath would normally be defined but if someone deleted it by control interface - it is better to revert to the default than fail */ - if (cnt->conf.imagepath) - imagepath = cnt->conf.imagepath; - else - imagepath = DEF_IMAGEPATH; - - mystrftime(cnt, filename, sizeof(filename), imagepath, currenttime_tm, NULL, 0); - snprintf(fullfilename, PATH_MAX, "%s/%s.%s", cnt->conf.filepath, filename, imageext(cnt)); - - put_picture(cnt, fullfilename, newimg, FTYPE_IMAGE); - } + char fullfilename[PATH_MAX]; + char filename[PATH_MAX]; + + if (cnt->new_img & NEWIMG_ON) { + const char *imagepath; + + /* conf.imagepath would normally be defined but if someone deleted it by control interface + it is better to revert to the default than fail */ + if (cnt->conf.imagepath) + imagepath = cnt->conf.imagepath; + else + imagepath = DEF_IMAGEPATH; + + mystrftime(cnt, filename, sizeof(filename), imagepath, currenttime_tm, NULL, 0); + snprintf(fullfilename, PATH_MAX, "%s/%s.%s", cnt->conf.filepath, filename, imageext(cnt)); + + put_picture(cnt, fullfilename, newimg, FTYPE_IMAGE); + } } static void event_imagem_detect(struct context *cnt, int type ATTRIBUTE_UNUSED, unsigned char *newimg ATTRIBUTE_UNUSED, char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED, struct tm *currenttime_tm) { - struct config *conf = &cnt->conf; - char fullfilenamem[PATH_MAX]; - char filename[PATH_MAX]; - char filenamem[PATH_MAX]; - - if (conf->motion_img) { - const char *imagepath; - - /* conf.imagepath would normally be defined but if someone deleted it by control interface - it is better to revert to the default than fail */ - if (cnt->conf.imagepath) - imagepath = cnt->conf.imagepath; - else - imagepath = DEF_IMAGEPATH; - - mystrftime(cnt, filename, sizeof(filename), imagepath, currenttime_tm, NULL, 0); - /* motion images gets same name as normal images plus an appended 'm' */ - snprintf(filenamem, PATH_MAX, "%sm", filename); - snprintf(fullfilenamem, PATH_MAX, "%s/%s.%s", cnt->conf.filepath, filenamem, imageext(cnt)); - - put_picture(cnt, fullfilenamem, cnt->imgs.out, FTYPE_IMAGE_MOTION); - } + struct config *conf = &cnt->conf; + char fullfilenamem[PATH_MAX]; + char filename[PATH_MAX]; + char filenamem[PATH_MAX]; + + if (conf->motion_img) { + const char *imagepath; + + /* conf.imagepath would normally be defined but if someone deleted it by control interface + it is better to revert to the default than fail */ + if (cnt->conf.imagepath) + imagepath = cnt->conf.imagepath; + else + imagepath = DEF_IMAGEPATH; + + mystrftime(cnt, filename, sizeof(filename), imagepath, currenttime_tm, NULL, 0); + /* motion images gets same name as normal images plus an appended 'm' */ + snprintf(filenamem, PATH_MAX, "%sm", filename); + snprintf(fullfilenamem, PATH_MAX, "%s/%s.%s", cnt->conf.filepath, filenamem, imageext(cnt)); + + put_picture(cnt, fullfilenamem, cnt->imgs.out, FTYPE_IMAGE_MOTION); + } } static void event_image_snapshot(struct context *cnt, int type ATTRIBUTE_UNUSED, unsigned char *img, char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED, struct tm *currenttime_tm) { - char fullfilename[PATH_MAX]; - - if (strcmp(cnt->conf.snappath, "lastsnap")) { - char filename[PATH_MAX]; - char filepath[PATH_MAX]; - char linkpath[PATH_MAX]; - const char *snappath; - /* conf.snappath would normally be defined but if someone deleted it by control interface - it is better to revert to the default than fail */ - if (cnt->conf.snappath) - snappath = cnt->conf.snappath; - else - snappath = DEF_SNAPPATH; - - mystrftime(cnt, filepath, sizeof(filepath), snappath, currenttime_tm, NULL, 0); - snprintf(filename, PATH_MAX, "%s.%s", filepath, imageext(cnt)); - snprintf(fullfilename, PATH_MAX, "%s/%s", cnt->conf.filepath, filename); - put_picture(cnt, fullfilename, img, FTYPE_IMAGE_SNAPSHOT); - - /* Update symbolic link *after* image has been written so that - the link always points to a valid file. */ - snprintf(linkpath, PATH_MAX, "%s/lastsnap.%s", cnt->conf.filepath, imageext(cnt)); - remove(linkpath); - if (symlink(filename, linkpath)) { - motion_log(LOG_ERR, 1, "%s: Could not create symbolic link [%s]", - __FUNCTION__, filename); - return; - } - } else { - snprintf(fullfilename, PATH_MAX, "%s/lastsnap.%s", cnt->conf.filepath, imageext(cnt)); - remove(fullfilename); - put_picture(cnt, fullfilename, img, FTYPE_IMAGE_SNAPSHOT); - } - - cnt->snapshot = 0; + char fullfilename[PATH_MAX]; + + if (strcmp(cnt->conf.snappath, "lastsnap")) { + char filename[PATH_MAX]; + char filepath[PATH_MAX]; + char linkpath[PATH_MAX]; + const char *snappath; + /* conf.snappath would normally be defined but if someone deleted it by control interface + it is better to revert to the default than fail */ + if (cnt->conf.snappath) + snappath = cnt->conf.snappath; + else + snappath = DEF_SNAPPATH; + + mystrftime(cnt, filepath, sizeof(filepath), snappath, currenttime_tm, NULL, 0); + snprintf(filename, PATH_MAX, "%s.%s", filepath, imageext(cnt)); + snprintf(fullfilename, PATH_MAX, "%s/%s", cnt->conf.filepath, filename); + put_picture(cnt, fullfilename, img, FTYPE_IMAGE_SNAPSHOT); + + /* Update symbolic link *after* image has been written so that + the link always points to a valid file. */ + snprintf(linkpath, PATH_MAX, "%s/lastsnap.%s", cnt->conf.filepath, imageext(cnt)); + remove(linkpath); + + if (symlink(filename, linkpath)) { + motion_log(LOG_ERR, 1, "%s: Could not create symbolic link [%s]", + __FUNCTION__, filename); + return; + } + } else { + snprintf(fullfilename, PATH_MAX, "%s/lastsnap.%s", cnt->conf.filepath, imageext(cnt)); + remove(fullfilename); + put_picture(cnt, fullfilename, img, FTYPE_IMAGE_SNAPSHOT); + } + + cnt->snapshot = 0; } static void event_camera_lost(struct context *cnt, int type ATTRIBUTE_UNUSED, unsigned char *img ATTRIBUTE_UNUSED, char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED, struct tm *currenttime_tm ATTRIBUTE_UNUSED) { - if (cnt->conf.on_camera_lost) - exec_command(cnt, cnt->conf.on_camera_lost, NULL, 0); + if (cnt->conf.on_camera_lost) + exec_command(cnt, cnt->conf.on_camera_lost, NULL, 0); } #ifdef HAVE_FFMPEG static void grey2yuv420p(unsigned char *u, unsigned char *v, int width, int height) { - memset(u, 128, width * height / 4); - memset(v, 128, width * height / 4); + memset(u, 128, width * height / 4); + memset(v, 128, width * height / 4); } static void on_movie_end_command(struct context *cnt, int type ATTRIBUTE_UNUSED, unsigned char *dummy ATTRIBUTE_UNUSED, char *filename, void *arg, struct tm *tm ATTRIBUTE_UNUSED) { - int filetype = (unsigned long) arg; + int filetype = (unsigned long) arg; - if ((filetype & FTYPE_MPEG_ANY) && cnt->conf.on_movie_end) - exec_command(cnt, cnt->conf.on_movie_end, filename, filetype); + if ((filetype & FTYPE_MPEG_ANY) && cnt->conf.on_movie_end) + exec_command(cnt, cnt->conf.on_movie_end, filename, filetype); } static void event_ffmpeg_newfile(struct context *cnt, int type ATTRIBUTE_UNUSED, unsigned char *img, char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED, struct tm *currenttime_tm) { - int width = cnt->imgs.width; - int height = cnt->imgs.height; - unsigned char *convbuf, *y, *u, *v; - char stamp[PATH_MAX]; - const char *moviepath; - - if (!cnt->conf.ffmpeg_output && !cnt->conf.ffmpeg_output_debug) - return; - - cnt->movie_last_shot = -1; - cnt->movie_fps = cnt->lastrate; - - if (debug_level >= CAMERA_DEBUG) - motion_log(LOG_DEBUG, 0, "%s FPS %d", __FUNCTION__, cnt->movie_fps); - - if (cnt->movie_fps > 30) - cnt->movie_fps = 30; - if (cnt->movie_fps < 2) - cnt->movie_fps = 2; - - /* conf.mpegpath would normally be defined but if someone deleted it by control interface - it is better to revert to the default than fail */ - if (cnt->conf.moviepath) - moviepath = cnt->conf.moviepath; - else - moviepath = DEF_MOVIEPATH; - - mystrftime(cnt, stamp, sizeof(stamp), moviepath, currenttime_tm, NULL, 0); - - /* motion movies get the same name as normal movies plus an appended 'm' */ - /* PATH_MAX - 4 to allow for .mpg to be appended without overflow */ - snprintf(cnt->motionfilename, PATH_MAX - 4, "%s/%sm", cnt->conf.filepath, stamp); - snprintf(cnt->newfilename, PATH_MAX - 4, "%s/%s", cnt->conf.filepath, stamp); - - if (cnt->conf.ffmpeg_output) { - if (cnt->imgs.type == VIDEO_PALETTE_GREY) { - convbuf = mymalloc((width * height) / 2); - y = img; - u = convbuf; - v = convbuf + (width * height) / 4; - grey2yuv420p(u, v, width, height); - } else { - convbuf = NULL; - y = img; - u = img + width * height; - v = u + (width * height) / 4; - } - if ( (cnt->ffmpeg_output = - ffmpeg_open((char *)cnt->conf.ffmpeg_video_codec, cnt->newfilename, y, u, v, - cnt->imgs.width, cnt->imgs.height, cnt->movie_fps, cnt->conf.ffmpeg_bps, - cnt->conf.ffmpeg_vbr)) == NULL) { - motion_log(LOG_ERR, 1, "s%: ffopen_open error creating (new) file [%s]", - __FUNCTION__, cnt->newfilename); - cnt->finish = 1; - return; - } - ((struct ffmpeg *)cnt->ffmpeg_output)->udata = convbuf; - event(cnt, EVENT_FILECREATE, NULL, cnt->newfilename, (void *)FTYPE_MPEG, NULL); - } - if (cnt->conf.ffmpeg_output_debug) { - if (cnt->imgs.type == VIDEO_PALETTE_GREY) { - convbuf = mymalloc((width * height) / 2); - y = cnt->imgs.out; - u = convbuf; - v = convbuf + (width * height) / 4; - grey2yuv420p(u, v, width, height); - } else { - y = cnt->imgs.out; - u = cnt->imgs.out + width *height; - v = u + (width * height) / 4; - convbuf = NULL; - } - - if ( (cnt->ffmpeg_output_debug = - ffmpeg_open((char *)cnt->conf.ffmpeg_video_codec, cnt->motionfilename, y, u, v, - cnt->imgs.width, cnt->imgs.height, cnt->movie_fps, cnt->conf.ffmpeg_bps, - cnt->conf.ffmpeg_vbr)) == NULL){ - motion_log(LOG_ERR, 1, "%s: ffopen_open error creating (motion) file [%s]", - __FUNCTION__, cnt->motionfilename); - cnt->finish = 1; - return; - } - cnt->ffmpeg_output_debug->udata = convbuf; - event(cnt, EVENT_FILECREATE, NULL, cnt->motionfilename, (void *)FTYPE_MPEG_MOTION, NULL); - } + int width = cnt->imgs.width; + int height = cnt->imgs.height; + unsigned char *convbuf, *y, *u, *v; + char stamp[PATH_MAX]; + const char *moviepath; + + if (!cnt->conf.ffmpeg_output && !cnt->conf.ffmpeg_output_debug) + return; + + cnt->movie_last_shot = -1; + cnt->movie_fps = cnt->lastrate; + + if (debug_level >= CAMERA_DEBUG) + motion_log(LOG_DEBUG, 0, "%s FPS %d", __FUNCTION__, cnt->movie_fps); + + if (cnt->movie_fps > 30) + cnt->movie_fps = 30; + if (cnt->movie_fps < 2) + cnt->movie_fps = 2; + + /* conf.mpegpath would normally be defined but if someone deleted it by control interface + it is better to revert to the default than fail */ + if (cnt->conf.moviepath) + moviepath = cnt->conf.moviepath; + else + moviepath = DEF_MOVIEPATH; + + mystrftime(cnt, stamp, sizeof(stamp), moviepath, currenttime_tm, NULL, 0); + + /* motion movies get the same name as normal movies plus an appended 'm' */ + /* PATH_MAX - 4 to allow for .mpg to be appended without overflow */ + snprintf(cnt->motionfilename, PATH_MAX - 4, "%s/%sm", cnt->conf.filepath, stamp); + snprintf(cnt->newfilename, PATH_MAX - 4, "%s/%s", cnt->conf.filepath, stamp); + + if (cnt->conf.ffmpeg_output) { + if (cnt->imgs.type == VIDEO_PALETTE_GREY) { + convbuf = mymalloc((width * height) / 2); + y = img; + u = convbuf; + v = convbuf + (width * height) / 4; + grey2yuv420p(u, v, width, height); + } else { + convbuf = NULL; + y = img; + u = img + width * height; + v = u + (width * height) / 4; + } + + if ((cnt->ffmpeg_output = + ffmpeg_open((char *)cnt->conf.ffmpeg_video_codec, cnt->newfilename, y, u, v, + cnt->imgs.width, cnt->imgs.height, cnt->movie_fps, cnt->conf.ffmpeg_bps, + cnt->conf.ffmpeg_vbr)) == NULL) { + motion_log(LOG_ERR, 1, "s%: ffopen_open error creating (new) file [%s]", + __FUNCTION__, cnt->newfilename); + cnt->finish = 1; + return; + } + + ((struct ffmpeg *)cnt->ffmpeg_output)->udata = convbuf; + event(cnt, EVENT_FILECREATE, NULL, cnt->newfilename, (void *)FTYPE_MPEG, NULL); + } + + if (cnt->conf.ffmpeg_output_debug) { + if (cnt->imgs.type == VIDEO_PALETTE_GREY) { + convbuf = mymalloc((width * height) / 2); + y = cnt->imgs.out; + u = convbuf; + v = convbuf + (width * height) / 4; + grey2yuv420p(u, v, width, height); + } else { + y = cnt->imgs.out; + u = cnt->imgs.out + width *height; + v = u + (width * height) / 4; + convbuf = NULL; + } + + if ((cnt->ffmpeg_output_debug = + ffmpeg_open((char *)cnt->conf.ffmpeg_video_codec, cnt->motionfilename, y, u, v, + cnt->imgs.width, cnt->imgs.height, cnt->movie_fps, cnt->conf.ffmpeg_bps, + cnt->conf.ffmpeg_vbr)) == NULL) { + motion_log(LOG_ERR, 1, "%s: ffopen_open error creating (motion) file [%s]", + __FUNCTION__, cnt->motionfilename); + cnt->finish = 1; + return; + } + + cnt->ffmpeg_output_debug->udata = convbuf; + event(cnt, EVENT_FILECREATE, NULL, cnt->motionfilename, (void *)FTYPE_MPEG_MOTION, NULL); + } } static void event_ffmpeg_timelapse(struct context *cnt, @@ -448,97 +456,97 @@ static void event_ffmpeg_timelapse(struct context *cnt, char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED, struct tm *currenttime_tm) { - int width = cnt->imgs.width; - int height = cnt->imgs.height; - unsigned char *convbuf, *y, *u, *v; - - if (!cnt->ffmpeg_timelapse) { - char tmp[PATH_MAX]; - const char *timepath; - - /* conf.timepath would normally be defined but if someone deleted it by control interface - it is better to revert to the default than fail */ - if (cnt->conf.timepath) - timepath = cnt->conf.timepath; - else - timepath = DEF_TIMEPATH; - - mystrftime(cnt, tmp, sizeof(tmp), timepath, currenttime_tm, NULL, 0); - - /* PATH_MAX - 4 to allow for .mpg to be appended without overflow */ - snprintf(cnt->timelapsefilename, PATH_MAX - 4, "%s/%s", cnt->conf.filepath, tmp); - - if (cnt->imgs.type == VIDEO_PALETTE_GREY) { - convbuf = mymalloc((width * height) / 2); - y = img; - u = convbuf; - v = convbuf + (width * height) / 4; - grey2yuv420p(u, v, width, height); - } else { - convbuf = NULL; - y = img; - u = img + width * height; - v = u + (width * height) / 4; - } - - if ( (cnt->ffmpeg_timelapse = - ffmpeg_open((char *)TIMELAPSE_CODEC, cnt->timelapsefilename, y, u, v, - cnt->imgs.width, cnt->imgs.height, 24, cnt->conf.ffmpeg_bps, - cnt->conf.ffmpeg_vbr)) == NULL) { - motion_log(LOG_ERR, 1, "%s: ffopen_open error creating (timelapse) file [%s]", - __FUNCTION__, cnt->timelapsefilename); - cnt->finish = 1; - return; - } - - cnt->ffmpeg_timelapse->udata = convbuf; - event(cnt, EVENT_FILECREATE, NULL, cnt->timelapsefilename, (void *)FTYPE_MPEG_TIMELAPSE, NULL); - } - - y = img; - - if (cnt->imgs.type == VIDEO_PALETTE_GREY) - u = cnt->ffmpeg_timelapse->udata; - else - u = img + width * height; - - v = u + (width * height) / 4; - if (ffmpeg_put_other_image(cnt->ffmpeg_timelapse, y, u, v) == -1){ - cnt->finish = 1; - cnt->restart = 0; - } - + int width = cnt->imgs.width; + int height = cnt->imgs.height; + unsigned char *convbuf, *y, *u, *v; + + if (!cnt->ffmpeg_timelapse) { + char tmp[PATH_MAX]; + const char *timepath; + + /* conf.timepath would normally be defined but if someone deleted it by control interface + it is better to revert to the default than fail */ + if (cnt->conf.timepath) + timepath = cnt->conf.timepath; + else + timepath = DEF_TIMEPATH; + + mystrftime(cnt, tmp, sizeof(tmp), timepath, currenttime_tm, NULL, 0); + + /* PATH_MAX - 4 to allow for .mpg to be appended without overflow */ + snprintf(cnt->timelapsefilename, PATH_MAX - 4, "%s/%s", cnt->conf.filepath, tmp); + + if (cnt->imgs.type == VIDEO_PALETTE_GREY) { + convbuf = mymalloc((width * height) / 2); + y = img; + u = convbuf; + v = convbuf + (width * height) / 4; + grey2yuv420p(u, v, width, height); + } else { + convbuf = NULL; + y = img; + u = img + width * height; + v = u + (width * height) / 4; + } + + if ((cnt->ffmpeg_timelapse = + ffmpeg_open((char *)TIMELAPSE_CODEC, cnt->timelapsefilename, y, u, v, + cnt->imgs.width, cnt->imgs.height, 24, cnt->conf.ffmpeg_bps, + cnt->conf.ffmpeg_vbr)) == NULL) { + motion_log(LOG_ERR, 1, "%s: ffopen_open error creating (timelapse) file [%s]", + __FUNCTION__, cnt->timelapsefilename); + cnt->finish = 1; + return; + } + + cnt->ffmpeg_timelapse->udata = convbuf; + event(cnt, EVENT_FILECREATE, NULL, cnt->timelapsefilename, (void *)FTYPE_MPEG_TIMELAPSE, NULL); + } + + y = img; + + if (cnt->imgs.type == VIDEO_PALETTE_GREY) + u = cnt->ffmpeg_timelapse->udata; + else + u = img + width * height; + + v = u + (width * height) / 4; + + if (ffmpeg_put_other_image(cnt->ffmpeg_timelapse, y, u, v) == -1) { + cnt->finish = 1; + cnt->restart = 0; + } + } static void event_ffmpeg_put(struct context *cnt, int type ATTRIBUTE_UNUSED, unsigned char *img, char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) { - if (cnt->ffmpeg_output) - { - int width = cnt->imgs.width; - int height = cnt->imgs.height; - unsigned char *y = img; - unsigned char *u, *v; - - if (cnt->imgs.type == VIDEO_PALETTE_GREY) - u = cnt->ffmpeg_timelapse->udata; - else - u = y + (width * height); - - v = u + (width * height) / 4; - if (ffmpeg_put_other_image(cnt->ffmpeg_output, y, u, v) == -1){ - cnt->finish = 1; - cnt->restart = 0; - } - } - - if (cnt->ffmpeg_output_debug) { - if (ffmpeg_put_image(cnt->ffmpeg_output_debug) == -1){ - cnt->finish = 1; - cnt->restart = 0; - } - } + if (cnt->ffmpeg_output) { + int width = cnt->imgs.width; + int height = cnt->imgs.height; + unsigned char *y = img; + unsigned char *u, *v; + + if (cnt->imgs.type == VIDEO_PALETTE_GREY) + u = cnt->ffmpeg_timelapse->udata; + else + u = y + (width * height); + + v = u + (width * height) / 4; + if (ffmpeg_put_other_image(cnt->ffmpeg_output, y, u, v) == -1) { + cnt->finish = 1; + cnt->restart = 0; + } + } + + if (cnt->ffmpeg_output_debug) { + if (ffmpeg_put_image(cnt->ffmpeg_output_debug) == -1) { + cnt->finish = 1; + cnt->restart = 0; + } + } } static void event_ffmpeg_closefile(struct context *cnt, @@ -546,23 +554,26 @@ static void event_ffmpeg_closefile(struct context *cnt, char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) { - - if (cnt->ffmpeg_output) { - if (cnt->ffmpeg_output->udata) - free(cnt->ffmpeg_output->udata); - ffmpeg_close(cnt->ffmpeg_output); - cnt->ffmpeg_output = NULL; - - event(cnt, EVENT_FILECLOSE, NULL, cnt->newfilename, (void *)FTYPE_MPEG, NULL); - } - if (cnt->ffmpeg_output_debug) { - if (cnt->ffmpeg_output_debug->udata) - free(cnt->ffmpeg_output_debug->udata); - ffmpeg_close(cnt->ffmpeg_output_debug); - cnt->ffmpeg_output_debug = NULL; - - event(cnt, EVENT_FILECLOSE, NULL, cnt->motionfilename, (void *)FTYPE_MPEG_MOTION, NULL); - } + + if (cnt->ffmpeg_output) { + if (cnt->ffmpeg_output->udata) + free(cnt->ffmpeg_output->udata); + + ffmpeg_close(cnt->ffmpeg_output); + cnt->ffmpeg_output = NULL; + + event(cnt, EVENT_FILECLOSE, NULL, cnt->newfilename, (void *)FTYPE_MPEG, NULL); + } + + if (cnt->ffmpeg_output_debug) { + if (cnt->ffmpeg_output_debug->udata) + free(cnt->ffmpeg_output_debug->udata); + + ffmpeg_close(cnt->ffmpeg_output_debug); + cnt->ffmpeg_output_debug = NULL; + + event(cnt, EVENT_FILECLOSE, NULL, cnt->motionfilename, (void *)FTYPE_MPEG_MOTION, NULL); + } } static void event_ffmpeg_timelapseend(struct context *cnt, @@ -570,127 +581,128 @@ static void event_ffmpeg_timelapseend(struct context *cnt, char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) { - if (cnt->ffmpeg_timelapse) { - if (cnt->ffmpeg_timelapse->udata) - free(cnt->ffmpeg_timelapse->udata); - ffmpeg_close(cnt->ffmpeg_timelapse); - cnt->ffmpeg_timelapse = NULL; - - event(cnt, EVENT_FILECLOSE, NULL, cnt->timelapsefilename, (void *)FTYPE_MPEG_TIMELAPSE, NULL); - } + if (cnt->ffmpeg_timelapse) { + if (cnt->ffmpeg_timelapse->udata) + free(cnt->ffmpeg_timelapse->udata); + + ffmpeg_close(cnt->ffmpeg_timelapse); + cnt->ffmpeg_timelapse = NULL; + + event(cnt, EVENT_FILECLOSE, NULL, cnt->timelapsefilename, (void *)FTYPE_MPEG_TIMELAPSE, NULL); + } } #endif /* HAVE_FFMPEG */ /* - * Starting point for all events + * Starting point for all events */ struct event_handlers { - int type; - event_handler handler; + int type; + event_handler handler; }; struct event_handlers event_handlers[] = { #if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) - { - EVENT_FILECREATE, - event_sqlnewfile - }, + { + EVENT_FILECREATE, + event_sqlnewfile + }, #endif - { - EVENT_FILECREATE, - on_picture_save_command - }, - { - EVENT_FILECREATE, - event_newfile - }, - - { - EVENT_MOTION, - event_beep - }, - { - EVENT_MOTION, - on_motion_detected_command - }, - { - EVENT_AREA_DETECTED, - on_area_command - }, - { - EVENT_FIRSTMOTION, - on_event_start_command - }, - { - EVENT_ENDMOTION, - on_event_end_command - }, - { - EVENT_IMAGE_DETECTED, - event_image_detect - }, - { - EVENT_IMAGEM_DETECTED, - event_imagem_detect - }, - { - EVENT_IMAGE_SNAPSHOT, - event_image_snapshot - }, + { + EVENT_FILECREATE, + on_picture_save_command + }, + { + EVENT_FILECREATE, + event_newfile + }, + + { + EVENT_MOTION, + event_beep + }, + { + EVENT_MOTION, + on_motion_detected_command + }, + { + EVENT_AREA_DETECTED, + on_area_command + }, + { + EVENT_FIRSTMOTION, + on_event_start_command + }, + { + EVENT_ENDMOTION, + on_event_end_command + }, + { + EVENT_IMAGE_DETECTED, + event_image_detect + }, + { + EVENT_IMAGEM_DETECTED, + event_imagem_detect + }, + { + EVENT_IMAGE_SNAPSHOT, + event_image_snapshot + }, #ifndef WITHOUT_V4L #if (!defined(BSD)) - { - EVENT_IMAGE | EVENT_IMAGEM, - event_vid_putpipe - }, + { + EVENT_IMAGE | EVENT_IMAGEM, + event_vid_putpipe + }, #endif /* BSD */ #endif /* WITHOUT_V4L */ - { - EVENT_STREAM, - event_stream_put - }, + { + EVENT_STREAM, + event_stream_put + }, #ifdef HAVE_FFMPEG - { - EVENT_FIRSTMOTION, - event_ffmpeg_newfile - }, - { - EVENT_IMAGE_DETECTED, - event_ffmpeg_put - }, - { - EVENT_FFMPEG_PUT, - event_ffmpeg_put - }, - { - EVENT_ENDMOTION, - event_ffmpeg_closefile - }, - { - EVENT_TIMELAPSE, - event_ffmpeg_timelapse - }, - { - EVENT_TIMELAPSEEND, - event_ffmpeg_timelapseend - }, - { - EVENT_FILECLOSE, - on_movie_end_command - }, + { + EVENT_FIRSTMOTION, + event_ffmpeg_newfile + }, + { + EVENT_IMAGE_DETECTED, + event_ffmpeg_put + }, + { + EVENT_FFMPEG_PUT, + event_ffmpeg_put + }, + { + EVENT_ENDMOTION, + event_ffmpeg_closefile + }, + { + EVENT_TIMELAPSE, + event_ffmpeg_timelapse + }, + { + EVENT_TIMELAPSEEND, + event_ffmpeg_timelapseend + }, + { + EVENT_FILECLOSE, + on_movie_end_command + }, #endif /* HAVE_FFMPEG */ - { - EVENT_CAMERA_LOST, - event_camera_lost - }, - { - EVENT_STOP, - event_stop_stream - }, - {0, NULL} + { + EVENT_CAMERA_LOST, + event_camera_lost + }, + { + EVENT_STOP, + event_stop_stream + }, + {0, NULL} }; @@ -706,10 +718,10 @@ struct event_handlers event_handlers[] = { */ void event(struct context *cnt, int type, unsigned char *image, char *filename, void *eventdata, struct tm *tm) { - int i=-1; + int i=-1; - while (event_handlers[++i].handler) { - if (type == event_handlers[i].type) - event_handlers[i].handler(cnt, type, image, filename, eventdata, tm); - } + while (event_handlers[++i].handler) { + if (type == event_handlers[i].type) + event_handlers[i].handler(cnt, type, image, filename, eventdata, tm); + } } diff --git a/event.h b/event.h index b9e4ec8..2f7ccb3 100644 --- a/event.h +++ b/event.h @@ -1,11 +1,11 @@ /* - * event.h + * event.h * - * Include file for event.c + * Include file for event.c * - * Copyright Jeroen Vreeken, 2002 - * This software is distributed under the GNU Public License Version 2 - * see also the file 'COPYING'. + * Copyright Jeroen Vreeken, 2002 + * This software is distributed under the GNU Public License Version 2 + * see also the file 'COPYING'. * */ #ifndef _INCLUDE_EVENT_H_ diff --git a/ffmpeg.c b/ffmpeg.c index f5fd8a3..830c924 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -20,20 +20,20 @@ * non-standard framerates. Previous builds contained a broken hack * that padded with B frames to obtain the correct framerate. */ -# define FFMPEG_NO_NONSTD_MPEG1 -# ifdef __GNUC__ +# define FFMPEG_NO_NONSTD_MPEG1 +# ifdef __GNUC__ /* #warning is a non-standard gcc extension */ -# warning ************************************************** -# warning Your version of FFmpeg is newer than version 0.4.8 -# warning Newer versions of ffmpeg do not support MPEG1 with -# warning non-standard framerate. MPEG1 will be disabled for -# warning normal video output. You can still use mpeg4 and -# warning and mpeg4ms which are both better in terms of size -# warning and quality. MPEG1 is always used for timelapse. -# warning Please read the Motion Guide for more information. -# warning Note that this is not an error message! -# warning ************************************************** -# endif /* __GNUC__ */ +# warning ************************************************** +# warning Your version of FFmpeg is newer than version 0.4.8 +# warning Newer versions of ffmpeg do not support MPEG1 with +# warning non-standard framerate. MPEG1 will be disabled for +# warning normal video output. You can still use mpeg4 and +# warning and mpeg4ms which are both better in terms of size +# warning and quality. MPEG1 is always used for timelapse. +# warning Please read the Motion Guide for more information. +# warning Note that this is not an error message! +# warning ************************************************** +# endif /* __GNUC__ */ #endif /* LIBAVCODEC_BUILD > 4680 */ @@ -42,7 +42,7 @@ * It now uses an AVPacket struct instead of direct parameters to the * function. The */ -# define FFMPEG_AVWRITEFRAME_NEWAPI +# define FFMPEG_AVWRITEFRAME_NEWAPI #endif /* LIBAVFORMAT_BUILD >= 4616 */ #if LIBAVFORMAT_BUILD >= 4629 @@ -50,9 +50,9 @@ * was changed to a pointer so changes to AVCodecContext shouldn't * break binary compatibility with AVStream. */ -# define AVSTREAM_CODEC_PTR(avs_ptr) (avs_ptr->codec) +# define AVSTREAM_CODEC_PTR(avs_ptr) (avs_ptr->codec) #else -# define AVSTREAM_CODEC_PTR(avs_ptr) (&avs_ptr->codec) +# define AVSTREAM_CODEC_PTR(avs_ptr) (&avs_ptr->codec) #endif /* LIBAVFORMAT_BUILD >= 4629 */ /* Name of custom file protocol for appending to existing files instead @@ -75,29 +75,30 @@ static unsigned char mpeg1_trailer[] = {0x00, 0x00, 0x01, 0xb7}; */ static int file_open_append(URLContext *h, const char *filename, int flags) { - const char *colon; - int access_flags, fd; - - /* Skip past the protocol part of filename. */ - colon = strchr(filename, ':'); - if (colon) { - filename = colon + 1; - } - - if (flags & URL_RDWR) { - access_flags = O_CREAT | O_APPEND | O_RDWR; - } else if (flags & URL_WRONLY) { - access_flags = O_CREAT | O_APPEND | O_WRONLY; - } else { - access_flags = O_RDONLY; - } - - fd = open(filename, access_flags, 0666); - if (fd < 0) { - return AVERROR(ENOENT); - } - h->priv_data = (void *)(size_t)fd; - return 0; + const char *colon; + int access_flags, fd; + + /* Skip past the protocol part of filename. */ + colon = strchr(filename, ':'); + + if (colon) + filename = colon + 1; + + + if (flags & URL_RDWR) { + access_flags = O_CREAT | O_APPEND | O_RDWR; + } else if (flags & URL_WRONLY) { + access_flags = O_CREAT | O_APPEND | O_WRONLY; + } else { + access_flags = O_RDONLY; + } + + fd = open(filename, access_flags, 0666); + if (fd < 0) + return AVERROR(ENOENT); + + h->priv_data = (void *)(size_t)fd; + return 0; } /* URLProtocol entry for the append file protocol, which we use for mpeg1 videos @@ -110,8 +111,8 @@ static int file_open_append(URLContext *h, const char *filename, int flags) * The remaining functions (for writing, seeking etc.) are set in ffmpeg_init. */ URLProtocol mpeg1_file_protocol = { - .name = APPEND_PROTO, - .url_open = file_open_append + .name = APPEND_PROTO, + .url_open = file_open_append }; @@ -130,58 +131,58 @@ URLProtocol mpeg1_file_protocol = { static int file_open(URLContext *h, const char *filename, int flags) { - int access_flags, fd; + int access_flags, fd; - av_strstart(filename, "file:", &filename); - - if (flags & URL_RDWR) { - access_flags = O_CREAT | O_TRUNC | O_RDWR; - } else if (flags & URL_WRONLY) { - access_flags = O_CREAT | O_TRUNC | O_WRONLY; - } else { - access_flags = O_RDONLY; - } + av_strstart(filename, "file:", &filename); + + if (flags & URL_RDWR) { + access_flags = O_CREAT | O_TRUNC | O_RDWR; + } else if (flags & URL_WRONLY) { + access_flags = O_CREAT | O_TRUNC | O_WRONLY; + } else { + access_flags = O_RDONLY; + } #ifdef O_BINARY - access_flags |= O_BINARY; + access_flags |= O_BINARY; #endif - fd = open(filename, access_flags, 0666); - if (fd < 0) - return AVERROR(ENOENT); - h->priv_data = (void *)(size_t)fd; - return 0; + fd = open(filename, access_flags, 0666); + if (fd < 0) + return AVERROR(ENOENT); + h->priv_data = (void *)(size_t)fd; + return 0; } static int file_read(URLContext *h, unsigned char *buf, int size) { - int fd = (size_t)h->priv_data; - return read(fd, buf, size); + int fd = (size_t)h->priv_data; + return read(fd, buf, size); } static int file_write(URLContext *h, unsigned char *buf, int size) { - int fd = (size_t)h->priv_data; - return write(fd, buf, size); + int fd = (size_t)h->priv_data; + return write(fd, buf, size); } static offset_t file_seek(URLContext *h, offset_t pos, int whence) { - int fd = (size_t)h->priv_data; - return lseek(fd, pos, whence); + int fd = (size_t)h->priv_data; + return lseek(fd, pos, whence); } static int file_close(URLContext *h) { - int fd = (size_t)h->priv_data; - return close(fd); + int fd = (size_t)h->priv_data; + return close(fd); } URLProtocol file_protocol = { - "file", - file_open, - file_read, - file_write, - file_seek, - file_close, + "file", + file_open, + file_read, + file_write, + file_seek, + file_close, }; #endif @@ -193,37 +194,37 @@ URLProtocol file_protocol = { static int mpeg1_write_trailer(AVFormatContext *s) { #if LIBAVFORMAT_BUILD >= (52<<16) - put_buffer(s->pb, mpeg1_trailer, 4); - put_flush_packet(s->pb); + put_buffer(s->pb, mpeg1_trailer, 4); + put_flush_packet(s->pb); #else - put_buffer(&s->pb, mpeg1_trailer, 4); - put_flush_packet(&s->pb); + put_buffer(&s->pb, mpeg1_trailer, 4); + put_flush_packet(&s->pb); #endif /* LIBAVFORMAT_BUILD >= (52<<16) */ - return 0; /* success */ + return 0; /* success */ } /* ffmpeg_init initializes for libavformat. */ void ffmpeg_init() { - motion_log(LOG_INFO, 0, "%s: ffmpeg LIBAVCODEC_BUILD %d LIBAVFORMAT_BUILD %d", - __FUNCTION__, LIBAVCODEC_BUILD, LIBAVFORMAT_BUILD); - av_register_all(); + motion_log(LOG_INFO, 0, "%s: ffmpeg LIBAVCODEC_BUILD %d LIBAVFORMAT_BUILD %d", + __FUNCTION__, LIBAVCODEC_BUILD, LIBAVFORMAT_BUILD); + av_register_all(); #if LIBAVCODEC_BUILD > 4680 - av_log_set_callback( (void *)ffmpeg_avcodec_log ); + av_log_set_callback((void *)ffmpeg_avcodec_log); #endif - /* Copy the functions to use for the append file protocol from the standard - * file protocol. - */ - mpeg1_file_protocol.url_read = file_protocol.url_read; - mpeg1_file_protocol.url_write = file_protocol.url_write; - mpeg1_file_protocol.url_seek = file_protocol.url_seek; - mpeg1_file_protocol.url_close = file_protocol.url_close; + /* Copy the functions to use for the append file protocol from the standard + * file protocol. + */ + mpeg1_file_protocol.url_read = file_protocol.url_read; + mpeg1_file_protocol.url_write = file_protocol.url_write; + mpeg1_file_protocol.url_seek = file_protocol.url_seek; + mpeg1_file_protocol.url_close = file_protocol.url_close; - /* Register the append file protocol. */ - register_protocol(&mpeg1_file_protocol); + /* Register the append file protocol. */ + register_protocol(&mpeg1_file_protocol); } /* Obtains the output format used for the specified codec. For mpeg4 codecs, @@ -232,76 +233,79 @@ void ffmpeg_init() */ static AVOutputFormat *get_oformat(const char *codec, char *filename) { - const char *ext; - AVOutputFormat *of = NULL; - - /* Here, we use guess_format to automatically setup the codec information. - * If we are using msmpeg4, manually set that codec here. - * We also dynamically add the file extension to the filename here. This was - * done to support both mpeg1 and mpeg4 codecs since they have different extensions. - */ - if ((strcmp(codec, TIMELAPSE_CODEC) == 0) + const char *ext; + AVOutputFormat *of = NULL; + + /* Here, we use guess_format to automatically setup the codec information. + * If we are using msmpeg4, manually set that codec here. + * We also dynamically add the file extension to the filename here. This was + * done to support both mpeg1 and mpeg4 codecs since they have different extensions. + */ + if ((strcmp(codec, TIMELAPSE_CODEC) == 0) #ifndef FFMPEG_NO_NONSTD_MPEG1 - || (strcmp(codec, "mpeg1") == 0) + || (strcmp(codec, "mpeg1") == 0) #endif - ) { - ext = ".mpg"; - /* We use "mpeg1video" for raw mpeg1 format. Using "mpeg" would - * result in a muxed output file, which isn't appropriate here. - */ - of = guess_format("mpeg1video", NULL, NULL); - if (of) { - /* But we want the trailer to be correctly written. */ - of->write_trailer = mpeg1_write_trailer; - } + ) { + ext = ".mpg"; + /* We use "mpeg1video" for raw mpeg1 format. Using "mpeg" would + * result in a muxed output file, which isn't appropriate here. + */ + of = guess_format("mpeg1video", NULL, NULL); + + /* But we want the trailer to be correctly written. */ + if (of) + of->write_trailer = mpeg1_write_trailer; + #ifdef FFMPEG_NO_NONSTD_MPEG1 - } else if (strcmp(codec, "mpeg1") == 0) { - motion_log(LOG_ERR, 0, "%s: *** mpeg1 support for normal videos has been disabled ***", - __FUNCTION__); - return NULL; + } else if (strcmp(codec, "mpeg1") == 0) { + motion_log(LOG_ERR, 0, "%s: *** mpeg1 support for normal videos has been disabled ***", + __FUNCTION__); + return NULL; #endif - } else if (strcmp(codec, "mpeg4") == 0) { - ext = ".avi"; - of = guess_format("avi", NULL, NULL); - } else if (strcmp(codec, "msmpeg4") == 0) { - ext = ".avi"; - of = guess_format("avi", NULL, NULL); - if (of) { - /* Manually override the codec id. */ - of->video_codec = CODEC_ID_MSMPEG4V2; - } - } else if (strcmp(codec, "swf") == 0) { - ext = ".swf"; - of = guess_format("swf", NULL, NULL); - } else if (strcmp(codec, "flv") == 0) { - ext = ".flv"; - of = guess_format("flv", NULL, NULL); - } else if (strcmp(codec, "ffv1") == 0) { - ext = ".avi"; - of = guess_format("avi", NULL, NULL); - if (of) { - /* Use the FFMPEG Lossless Video codec (experimental!). - Requires strict_std_compliance to be <= -2 */ - of->video_codec = CODEC_ID_FLV1; - } - } else if (strcmp(codec, "mov") == 0) { - ext = ".mov"; - of = guess_format("mov", NULL, NULL); - } else { - motion_log(LOG_ERR, 0, "%s: ffmpeg_video_codec option value %s is not supported", - __FUNCTION__, codec); - return NULL; - } - - if (!of) { - motion_log(LOG_ERR, 0, "%s: Could not guess format for %s", __FUNCTION__, codec); - return NULL; - } - - /* The 4 allows for ".avi" or ".mpg" to be appended */ - strncat(filename, ext, 4); - - return of; + } else if (strcmp(codec, "mpeg4") == 0) { + ext = ".avi"; + of = guess_format("avi", NULL, NULL); + } else if (strcmp(codec, "msmpeg4") == 0) { + ext = ".avi"; + of = guess_format("avi", NULL, NULL); + + /* Manually override the codec id. */ + if (of) + of->video_codec = CODEC_ID_MSMPEG4V2; + + } else if (strcmp(codec, "swf") == 0) { + ext = ".swf"; + of = guess_format("swf", NULL, NULL); + } else if (strcmp(codec, "flv") == 0) { + ext = ".flv"; + of = guess_format("flv", NULL, NULL); + } else if (strcmp(codec, "ffv1") == 0) { + ext = ".avi"; + of = guess_format("avi", NULL, NULL); + + /* Use the FFMPEG Lossless Video codec (experimental!). + * Requires strict_std_compliance to be <= -2 */ + if (of) + of->video_codec = CODEC_ID_FLV1; + + } else if (strcmp(codec, "mov") == 0) { + ext = ".mov"; + of = guess_format("mov", NULL, NULL); + } else { + motion_log(LOG_ERR, 0, "%s: ffmpeg_video_codec option value %s is not supported", + __FUNCTION__, codec); + return NULL; + } + + if (!of) { + motion_log(LOG_ERR, 0, "%s: Could not guess format for %s", __FUNCTION__, codec); + return NULL; + } + + /* The 4 allows for ".avi" or ".mpg" to be appended */ + strncat(filename, ext, 4); + + return of; } /* This function opens an mpeg file using the new libavformat method. Both mpeg1 @@ -313,217 +317,219 @@ struct ffmpeg *ffmpeg_open(char *ffmpeg_video_codec, char *filename, unsigned char *y, unsigned char *u, unsigned char *v, int width, int height, int rate, int bps, int vbr) { - AVCodecContext *c; - AVCodec *codec; - struct ffmpeg *ffmpeg; - int is_mpeg1; - - /* Allocate space for our ffmpeg structure. This structure contains all the - * codec and image information we need to generate movies. - * FIXME when motion exits we should close the movie to ensure that - * ffmpeg is freed. - */ - ffmpeg = mymalloc(sizeof(struct ffmpeg)); - memset(ffmpeg, 0, sizeof(struct ffmpeg)); - - ffmpeg->vbr = vbr; - - /* store codec name in ffmpeg->codec, with buffer overflow check */ - snprintf(ffmpeg->codec, sizeof(ffmpeg->codec), "%s", ffmpeg_video_codec); - - /* allocation the output media context */ - ffmpeg->oc = av_mallocz(sizeof(AVFormatContext)); - if (!ffmpeg->oc) { - motion_log(LOG_ERR, 1, "%s: Memory error while allocating output media context", __FUNCTION__); - ffmpeg_cleanups(ffmpeg); - return (NULL); - } - - /* Setup output format */ - ffmpeg->oc->oformat = get_oformat(ffmpeg_video_codec, filename); - if (!ffmpeg->oc->oformat) { - ffmpeg_cleanups(ffmpeg); - return NULL; - } - - snprintf(ffmpeg->oc->filename, sizeof(ffmpeg->oc->filename), "%s", filename); - - /* Create a new video stream and initialize the codecs */ - ffmpeg->video_st = NULL; - if (ffmpeg->oc->oformat->video_codec != CODEC_ID_NONE) { - ffmpeg->video_st = av_new_stream(ffmpeg->oc, 0); - if (!ffmpeg->video_st) { - motion_log(LOG_ERR, 1, "%s: av_new_stream - could not alloc stream", __FUNCTION__); - ffmpeg_cleanups(ffmpeg); - return (NULL); - } - } else { - /* We did not get a proper video codec. */ - motion_log(LOG_ERR, 0, "%s: Failed to obtain a proper video codec", __FUNCTION__); - ffmpeg_cleanups(ffmpeg); - return (NULL); - } - - ffmpeg->c = c = AVSTREAM_CODEC_PTR(ffmpeg->video_st); - c->codec_id = ffmpeg->oc->oformat->video_codec; - c->codec_type = CODEC_TYPE_VIDEO; - is_mpeg1 = c->codec_id == CODEC_ID_MPEG1VIDEO; - - if (strcmp(ffmpeg_video_codec, "ffv1") == 0) - c->strict_std_compliance = -2; - - /* Uncomment to allow non-standard framerates. */ - //c->strict_std_compliance = -1; - - /* Set default parameters */ - c->bit_rate = bps; - c->width = width; - c->height = height; + AVCodecContext *c; + AVCodec *codec; + struct ffmpeg *ffmpeg; + int is_mpeg1; + + /* Allocate space for our ffmpeg structure. This structure contains all the + * codec and image information we need to generate movies. + * FIXME when motion exits we should close the movie to ensure that + * ffmpeg is freed. + */ + ffmpeg = mymalloc(sizeof(struct ffmpeg)); + memset(ffmpeg, 0, sizeof(struct ffmpeg)); + + ffmpeg->vbr = vbr; + + /* store codec name in ffmpeg->codec, with buffer overflow check */ + snprintf(ffmpeg->codec, sizeof(ffmpeg->codec), "%s", ffmpeg_video_codec); + + /* allocation the output media context */ + ffmpeg->oc = av_mallocz(sizeof(AVFormatContext)); + if (!ffmpeg->oc) { + motion_log(LOG_ERR, 1, "%s: Memory error while allocating output media context", __FUNCTION__); + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + + /* Setup output format */ + ffmpeg->oc->oformat = get_oformat(ffmpeg_video_codec, filename); + if (!ffmpeg->oc->oformat) { + ffmpeg_cleanups(ffmpeg); + return NULL; + } + + snprintf(ffmpeg->oc->filename, sizeof(ffmpeg->oc->filename), "%s", filename); + + /* Create a new video stream and initialize the codecs */ + ffmpeg->video_st = NULL; + if (ffmpeg->oc->oformat->video_codec != CODEC_ID_NONE) { + ffmpeg->video_st = av_new_stream(ffmpeg->oc, 0); + + if (!ffmpeg->video_st) { + motion_log(LOG_ERR, 1, "%s: av_new_stream - could not alloc stream", __FUNCTION__); + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + } else { + /* We did not get a proper video codec. */ + motion_log(LOG_ERR, 0, "%s: Failed to obtain a proper video codec", __FUNCTION__); + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + + ffmpeg->c = c = AVSTREAM_CODEC_PTR(ffmpeg->video_st); + c->codec_id = ffmpeg->oc->oformat->video_codec; + c->codec_type = CODEC_TYPE_VIDEO; + is_mpeg1 = c->codec_id == CODEC_ID_MPEG1VIDEO; + + if (strcmp(ffmpeg_video_codec, "ffv1") == 0) + c->strict_std_compliance = -2; + + /* Uncomment to allow non-standard framerates. */ + //c->strict_std_compliance = -1; + + /* Set default parameters */ + c->bit_rate = bps; + c->width = width; + c->height = height; #if LIBAVCODEC_BUILD >= 4754 - /* frame rate = 1/time_base, so we set 1/rate, not rate/1 */ - c->time_base.num = 1; - c->time_base.den = rate; + /* frame rate = 1/time_base, so we set 1/rate, not rate/1 */ + c->time_base.num = 1; + c->time_base.den = rate; #else - c->frame_rate = rate; - c->frame_rate_base = 1; + c->frame_rate = rate; + c->frame_rate_base = 1; #endif /* LIBAVCODEC_BUILD >= 4754 */ - if (debug_level >= CAMERA_DEBUG) - motion_log(LOG_DEBUG, 0, "%s FPS %d", __FUNCTION__, rate); - - if (vbr) - c->flags |= CODEC_FLAG_QSCALE; - - /* Set codec specific parameters. */ - /* set intra frame distance in frames depending on codec */ - c->gop_size = is_mpeg1 ? 10 : 12; - - /* some formats want stream headers to be separate */ - if(!strcmp(ffmpeg->oc->oformat->name, "mp4") || - !strcmp(ffmpeg->oc->oformat->name, "mov") || - !strcmp(ffmpeg->oc->oformat->name, "3gp")) { - c->flags |= CODEC_FLAG_GLOBAL_HEADER; - } - - /* set the output parameters (must be done even if no parameters). */ - if (av_set_parameters(ffmpeg->oc, NULL) < 0) { - motion_log(LOG_ERR, 0, "%s: av_set_parameters error: Invalid output format parameters", - __FUNCTION__); - ffmpeg_cleanups(ffmpeg); - return (NULL); - } - - /* Dump the format settings. This shows how the various streams relate to each other */ - //dump_format(ffmpeg->oc, 0, filename, 1); - - /* Now that all the parameters are set, we can open the video - codec and allocate the necessary encode buffers */ - codec = avcodec_find_encoder(c->codec_id); - if (!codec) { - motion_log(LOG_ERR, 1, "%s: Codec not found", __FUNCTION__); - ffmpeg_cleanups(ffmpeg); - return (NULL); - } - - /* Set the picture format - need in ffmpeg starting round April-May 2005 */ - c->pix_fmt = PIX_FMT_YUV420P; - - /* Get a mutex lock. */ - pthread_mutex_lock(&global_lock); - - /* open the codec */ - if (avcodec_open(c, codec) < 0) { - /* Release the lock. */ - pthread_mutex_unlock(&global_lock); - motion_log(LOG_ERR, 1, "%s: avcodec_open - could not open codec", __FUNCTION__); - ffmpeg_cleanups(ffmpeg); - return (NULL); - } - - /* Release the lock. */ - pthread_mutex_unlock(&global_lock); - - - ffmpeg->video_outbuf = NULL; - if (!(ffmpeg->oc->oformat->flags & AVFMT_RAWPICTURE)) { - /* allocate output buffer */ - /* XXX: API change will be done */ - /* ffmpeg->video_outbuf_size = 200000 */ - ffmpeg->video_outbuf_size = ffmpeg->c->width * 256; - ffmpeg->video_outbuf = mymalloc(ffmpeg->video_outbuf_size); - } - - /* allocate the encoded raw picture */ - ffmpeg->picture = avcodec_alloc_frame(); - if (!ffmpeg->picture) { - motion_log(LOG_ERR, 1, "%s: avcodec_alloc_frame - could not alloc frame", __FUNCTION__); - ffmpeg_cleanups(ffmpeg); - return (NULL); - } - - /* set variable bitrate if requested */ - if (ffmpeg->vbr) { - ffmpeg->picture->quality = ffmpeg->vbr; - } - - /* set the frame data */ - ffmpeg->picture->data[0] = y; - ffmpeg->picture->data[1] = u; - ffmpeg->picture->data[2] = v; - ffmpeg->picture->linesize[0] = ffmpeg->c->width; - ffmpeg->picture->linesize[1] = ffmpeg->c->width / 2; - ffmpeg->picture->linesize[2] = ffmpeg->c->width / 2; - - /* open the output file, if needed */ - if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { - char file_proto[256]; - - /* Use append file protocol for mpeg1, to get the append behavior from - * url_fopen, but no protocol (=> default) for other codecs. - */ - if(is_mpeg1) { - snprintf(file_proto, sizeof(file_proto), APPEND_PROTO ":%s", filename); - } else { - snprintf(file_proto, sizeof(file_proto), "%s", filename); - } - - if (url_fopen(&ffmpeg->oc->pb, file_proto, URL_WRONLY) < 0) { - /* path did not exist? */ - if (errno == ENOENT) { - /* create path for file (don't use file_proto)... */ - if (create_path(filename) == -1) { - ffmpeg_cleanups(ffmpeg); - return (NULL); - } - - /* and retry opening the file (use file_proto) */ - if (url_fopen(&ffmpeg->oc->pb, file_proto, URL_WRONLY) < 0) { - motion_log(LOG_ERR, 1, "%s: url_fopen - error opening file %s", - __FUNCTION__, filename); - ffmpeg_cleanups(ffmpeg); - return (NULL); - } - /* Permission denied */ - } else if (errno == EACCES) { - motion_log(LOG_ERR, 1, - "%s: url_fopen - error opening file %s" - " ... check access rights to target directory", - __FUNCTION__, filename); - /* create path for file... */ - ffmpeg_cleanups(ffmpeg); - return (NULL); - } else { - motion_log(LOG_ERR, 1, "%s: Error opening file %s", __FUNCTION__, filename); - ffmpeg_cleanups(ffmpeg); - return (NULL); - } - } - } - - /* write the stream header, if any */ - av_write_header(ffmpeg->oc); - - return ffmpeg; + if (debug_level >= CAMERA_DEBUG) + motion_log(LOG_DEBUG, 0, "%s FPS %d", __FUNCTION__, rate); + + if (vbr) + c->flags |= CODEC_FLAG_QSCALE; + + /* Set codec specific parameters. */ + /* set intra frame distance in frames depending on codec */ + c->gop_size = is_mpeg1 ? 10 : 12; + + /* some formats want stream headers to be separate */ + if (!strcmp(ffmpeg->oc->oformat->name, "mp4") || + !strcmp(ffmpeg->oc->oformat->name, "mov") || + !strcmp(ffmpeg->oc->oformat->name, "3gp")) { + c->flags |= CODEC_FLAG_GLOBAL_HEADER; + } + + /* set the output parameters (must be done even if no parameters). */ + if (av_set_parameters(ffmpeg->oc, NULL) < 0) { + motion_log(LOG_ERR, 0, "%s: av_set_parameters error: Invalid output format parameters", + __FUNCTION__); + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + + /* Dump the format settings. This shows how the various streams relate to each other */ + //dump_format(ffmpeg->oc, 0, filename, 1); + + /* Now that all the parameters are set, we can open the video + codec and allocate the necessary encode buffers */ + codec = avcodec_find_encoder(c->codec_id); + + if (!codec) { + motion_log(LOG_ERR, 1, "%s: Codec not found", __FUNCTION__); + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + + /* Set the picture format - need in ffmpeg starting round April-May 2005 */ + c->pix_fmt = PIX_FMT_YUV420P; + + /* Get a mutex lock. */ + pthread_mutex_lock(&global_lock); + + /* open the codec */ + if (avcodec_open(c, codec) < 0) { + /* Release the lock. */ + pthread_mutex_unlock(&global_lock); + motion_log(LOG_ERR, 1, "%s: avcodec_open - could not open codec", __FUNCTION__); + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + + /* Release the lock. */ + pthread_mutex_unlock(&global_lock); + + ffmpeg->video_outbuf = NULL; + + if (!(ffmpeg->oc->oformat->flags & AVFMT_RAWPICTURE)) { + /* allocate output buffer */ + /* XXX: API change will be done */ + /* ffmpeg->video_outbuf_size = 200000 */ + ffmpeg->video_outbuf_size = ffmpeg->c->width * 256; + ffmpeg->video_outbuf = mymalloc(ffmpeg->video_outbuf_size); + } + + /* allocate the encoded raw picture */ + ffmpeg->picture = avcodec_alloc_frame(); + + if (!ffmpeg->picture) { + motion_log(LOG_ERR, 1, "%s: avcodec_alloc_frame - could not alloc frame", __FUNCTION__); + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + + /* set variable bitrate if requested */ + if (ffmpeg->vbr) + ffmpeg->picture->quality = ffmpeg->vbr; + + + /* set the frame data */ + ffmpeg->picture->data[0] = y; + ffmpeg->picture->data[1] = u; + ffmpeg->picture->data[2] = v; + ffmpeg->picture->linesize[0] = ffmpeg->c->width; + ffmpeg->picture->linesize[1] = ffmpeg->c->width / 2; + ffmpeg->picture->linesize[2] = ffmpeg->c->width / 2; + + /* open the output file, if needed */ + if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { + char file_proto[256]; + + /* Use append file protocol for mpeg1, to get the append behavior from + * url_fopen, but no protocol (=> default) for other codecs. + */ + if (is_mpeg1) + snprintf(file_proto, sizeof(file_proto), APPEND_PROTO ":%s", filename); + else + snprintf(file_proto, sizeof(file_proto), "%s", filename); + + if (url_fopen(&ffmpeg->oc->pb, file_proto, URL_WRONLY) < 0) { + /* path did not exist? */ + if (errno == ENOENT) { + /* create path for file (don't use file_proto)... */ + if (create_path(filename) == -1) { + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + + /* and retry opening the file (use file_proto) */ + if (url_fopen(&ffmpeg->oc->pb, file_proto, URL_WRONLY) < 0) { + motion_log(LOG_ERR, 1, "%s: url_fopen - error opening file %s", + __FUNCTION__, filename); + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + /* Permission denied */ + } else if (errno == EACCES) { + motion_log(LOG_ERR, 1, + "%s: url_fopen - error opening file %s" + " ... check access rights to target directory", + __FUNCTION__, filename); + /* create path for file... */ + ffmpeg_cleanups(ffmpeg); + return (NULL); + } else { + motion_log(LOG_ERR, 1, "%s: Error opening file %s", __FUNCTION__, filename); + ffmpeg_cleanups(ffmpeg); + return (NULL); + } + } + } + + /* write the stream header, if any */ + av_write_header(ffmpeg->oc); + + return ffmpeg; } /* @@ -531,100 +537,100 @@ struct ffmpeg *ffmpeg_open(char *ffmpeg_video_codec, char *filename, */ void ffmpeg_cleanups(struct ffmpeg *ffmpeg) { - unsigned int i; + unsigned int i; - /* close each codec */ - if (ffmpeg->video_st) { - pthread_mutex_lock(&global_lock); + /* close each codec */ + if (ffmpeg->video_st) { + pthread_mutex_lock(&global_lock); #if LIBAVCODEC_BUILD > 4680 - if (ffmpeg->video_st->codec->priv_data != NULL) + if (ffmpeg->video_st->codec->priv_data != NULL) #endif - avcodec_close(AVSTREAM_CODEC_PTR(ffmpeg->video_st)); - pthread_mutex_unlock(&global_lock); - av_freep(&ffmpeg->picture); - free(ffmpeg->video_outbuf); - } - - /* free the streams */ - for (i = 0; i < ffmpeg->oc->nb_streams; i++) { - av_freep(&ffmpeg->oc->streams[i]); - } + avcodec_close(AVSTREAM_CODEC_PTR(ffmpeg->video_st)); + pthread_mutex_unlock(&global_lock); + av_freep(&ffmpeg->picture); + free(ffmpeg->video_outbuf); + } + + /* free the streams */ + for (i = 0; i < ffmpeg->oc->nb_streams; i++) + av_freep(&ffmpeg->oc->streams[i]); + /* - if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { - // close the output file - if (ffmpeg->oc->pb) url_fclose(&ffmpeg->oc->pb); - } + if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { + // close the output file + if (ffmpeg->oc->pb) url_fclose(&ffmpeg->oc->pb); + } */ - /* free the stream */ - av_free(ffmpeg->oc); + /* free the stream */ + av_free(ffmpeg->oc); #if LIBAVFORMAT_BUILD >= 4629 - av_free(ffmpeg->c); + av_free(ffmpeg->c); #endif - free(ffmpeg); + free(ffmpeg); } /* Closes a video file. */ void ffmpeg_close(struct ffmpeg *ffmpeg) { - unsigned int i; - - /* close each codec */ - if (ffmpeg->video_st) { - pthread_mutex_lock(&global_lock); - avcodec_close(AVSTREAM_CODEC_PTR(ffmpeg->video_st)); - pthread_mutex_unlock(&global_lock); - av_freep(&ffmpeg->picture); - free(ffmpeg->video_outbuf); - } - - /* write the trailer, if any */ - av_write_trailer(ffmpeg->oc); - - /* free the streams */ - for (i = 0; i < ffmpeg->oc->nb_streams; i++) { - av_freep(&ffmpeg->oc->streams[i]); - } - - if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { - /* close the output file */ + unsigned int i; + + /* close each codec */ + if (ffmpeg->video_st) { + pthread_mutex_lock(&global_lock); + avcodec_close(AVSTREAM_CODEC_PTR(ffmpeg->video_st)); + pthread_mutex_unlock(&global_lock); + av_freep(&ffmpeg->picture); + free(ffmpeg->video_outbuf); + } + + /* write the trailer, if any */ + av_write_trailer(ffmpeg->oc); + + /* free the streams */ + for (i = 0; i < ffmpeg->oc->nb_streams; i++) + av_freep(&ffmpeg->oc->streams[i]); + + + if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { + /* close the output file */ #if LIBAVFORMAT_BUILD >= (52<<16) - url_fclose(ffmpeg->oc->pb); + url_fclose(ffmpeg->oc->pb); #else - url_fclose(&ffmpeg->oc->pb); + url_fclose(&ffmpeg->oc->pb); #endif /* LIBAVFORMAT_BUILD >= (52<<16) */ - } + } - /* free the stream */ - av_free(ffmpeg->oc); + /* free the stream */ + av_free(ffmpeg->oc); #if LIBAVFORMAT_BUILD >= 4629 - av_free(ffmpeg->c); + av_free(ffmpeg->c); #endif - free(ffmpeg); + free(ffmpeg); } /* Puts the image pointed to by ffmpeg->picture. */ int ffmpeg_put_image(struct ffmpeg *ffmpeg) { - return ffmpeg_put_frame(ffmpeg, ffmpeg->picture); + return ffmpeg_put_frame(ffmpeg, ffmpeg->picture); } /* Puts an arbitrary picture defined by y, u and v. */ int ffmpeg_put_other_image(struct ffmpeg *ffmpeg, unsigned char *y, unsigned char *u, unsigned char *v) { - AVFrame *picture; - int ret = 0; + AVFrame *picture; + int ret = 0; - /* allocate the encoded raw picture */ - picture = ffmpeg_prepare_frame(ffmpeg, y, u, v); + /* allocate the encoded raw picture */ + picture = ffmpeg_prepare_frame(ffmpeg, y, u, v); - if (picture) { - ret = ffmpeg_put_frame(ffmpeg, picture); - if (!ret) - av_free(picture); - } + if (picture) { + ret = ffmpeg_put_frame(ffmpeg, picture); + if (!ret) + av_free(picture); + } - return ret; + return ret; } /* Encodes and writes a video frame using the av_write_frame API. This is @@ -632,59 +638,61 @@ int ffmpeg_put_other_image(struct ffmpeg *ffmpeg, unsigned char *y, */ int ffmpeg_put_frame(struct ffmpeg *ffmpeg, AVFrame *pic) { - int out_size, ret; + int out_size, ret; #ifdef FFMPEG_AVWRITEFRAME_NEWAPI - AVPacket pkt; + AVPacket pkt; - av_init_packet(&pkt); /* init static structure */ - pkt.stream_index = ffmpeg->video_st->index; + av_init_packet(&pkt); /* init static structure */ + pkt.stream_index = ffmpeg->video_st->index; #endif /* FFMPEG_AVWRITEFRAME_NEWAPI */ - if (ffmpeg->oc->oformat->flags & AVFMT_RAWPICTURE) { - /* raw video case. The API will change slightly in the near future for that */ + if (ffmpeg->oc->oformat->flags & AVFMT_RAWPICTURE) { + /* raw video case. The API will change slightly in the near future for that */ #ifdef FFMPEG_AVWRITEFRAME_NEWAPI - pkt.flags |= PKT_FLAG_KEY; - pkt.data = (uint8_t *)pic; - pkt.size = sizeof(AVPicture); - ret = av_write_frame(ffmpeg->oc, &pkt); + pkt.flags |= PKT_FLAG_KEY; + pkt.data = (uint8_t *)pic; + pkt.size = sizeof(AVPicture); + ret = av_write_frame(ffmpeg->oc, &pkt); #else - ret = av_write_frame(ffmpeg->oc, ffmpeg->video_st->index, - (uint8_t *)pic, sizeof(AVPicture)); + ret = av_write_frame(ffmpeg->oc, ffmpeg->video_st->index, + (uint8_t *)pic, sizeof(AVPicture)); #endif /* FFMPEG_AVWRITEFRAME_NEWAPI */ - } else { - /* encode the image */ - out_size = avcodec_encode_video(AVSTREAM_CODEC_PTR(ffmpeg->video_st), - ffmpeg->video_outbuf, - ffmpeg->video_outbuf_size, pic); - - /* if zero size, it means the image was buffered */ - if (out_size != 0) { - /* write the compressed frame in the media file */ - /* XXX: in case of B frames, the pts is not yet valid */ + } else { + /* encode the image */ + out_size = avcodec_encode_video(AVSTREAM_CODEC_PTR(ffmpeg->video_st), + ffmpeg->video_outbuf, + ffmpeg->video_outbuf_size, pic); + + /* if zero size, it means the image was buffered */ + if (out_size != 0) { + /* write the compressed frame in the media file */ + /* XXX: in case of B frames, the pts is not yet valid */ #ifdef FFMPEG_AVWRITEFRAME_NEWAPI - pkt.pts = AVSTREAM_CODEC_PTR(ffmpeg->video_st)->coded_frame->pts; - if (AVSTREAM_CODEC_PTR(ffmpeg->video_st)->coded_frame->key_frame) { - pkt.flags |= PKT_FLAG_KEY; - } - pkt.data = ffmpeg->video_outbuf; - pkt.size = out_size; - ret = av_write_frame(ffmpeg->oc, &pkt); + pkt.pts = AVSTREAM_CODEC_PTR(ffmpeg->video_st)->coded_frame->pts; + + if (AVSTREAM_CODEC_PTR(ffmpeg->video_st)->coded_frame->key_frame) + pkt.flags |= PKT_FLAG_KEY; + + + pkt.data = ffmpeg->video_outbuf; + pkt.size = out_size; + ret = av_write_frame(ffmpeg->oc, &pkt); #else - ret = av_write_frame(ffmpeg->oc, ffmpeg->video_st->index, - ffmpeg->video_outbuf, out_size); + ret = av_write_frame(ffmpeg->oc, ffmpeg->video_st->index, + ffmpeg->video_outbuf, out_size); #endif /* FFMPEG_AVWRITEFRAME_NEWAPI */ - } else { - ret = 0; - } - } - - if (ret != 0) { - motion_log(LOG_ERR, 1, "%s: Error while writing video frame", __FUNCTION__); - ffmpeg_cleanups(ffmpeg); - return (-1); - } - - return ret; + } else { + ret = 0; + } + } + + if (ret != 0) { + motion_log(LOG_ERR, 1, "%s: Error while writing video frame", __FUNCTION__); + ffmpeg_cleanups(ffmpeg); + return (-1); + } + + return ret; } /* Allocates and prepares a picture frame by setting up the U, Y and V pointers in @@ -697,28 +705,28 @@ int ffmpeg_put_frame(struct ffmpeg *ffmpeg, AVFrame *pic) AVFrame *ffmpeg_prepare_frame(struct ffmpeg *ffmpeg, unsigned char *y, unsigned char *u, unsigned char *v) { - AVFrame *picture; - - picture = avcodec_alloc_frame(); - if (!picture) { - motion_log(LOG_ERR, 1, "%s: Could not alloc frame", __FUNCTION__); - return NULL; - } - - /* take care of variable bitrate setting */ - if (ffmpeg->vbr) { - picture->quality = ffmpeg->vbr; - } - - /* setup pointers and line widths */ - picture->data[0] = y; - picture->data[1] = u; - picture->data[2] = v; - picture->linesize[0] = ffmpeg->c->width; - picture->linesize[1] = ffmpeg->c->width / 2; - picture->linesize[2] = ffmpeg->c->width / 2; - - return picture; + AVFrame *picture; + + picture = avcodec_alloc_frame(); + + if (!picture) { + motion_log(LOG_ERR, 1, "%s: Could not alloc frame", __FUNCTION__); + return NULL; + } + + /* take care of variable bitrate setting */ + if (ffmpeg->vbr) + picture->quality = ffmpeg->vbr; + + /* setup pointers and line widths */ + picture->data[0] = y; + picture->data[1] = u; + picture->data[2] = v; + picture->linesize[0] = ffmpeg->c->width; + picture->linesize[1] = ffmpeg->c->width / 2; + picture->linesize[2] = ffmpeg->c->width / 2; + + return picture; } @@ -736,28 +744,29 @@ AVFrame *ffmpeg_prepare_frame(struct ffmpeg *ffmpeg, unsigned char *y, */ void ffmpeg_deinterlace(unsigned char *img, int width, int height) { - AVFrame *picture; - int width2 = width / 2; - - picture = avcodec_alloc_frame(); - if (!picture) { - motion_log(LOG_ERR, 1, "%s: Could not alloc frame", __FUNCTION__); - return; - } - - picture->data[0] = img; - picture->data[1] = img + width * height; - picture->data[2] = picture->data[1] + (width * height) / 4; - picture->linesize[0] = width; - picture->linesize[1] = width2; - picture->linesize[2] = width2; - - /* We assume using 'PIX_FMT_YUV420P' always */ - avpicture_deinterlace((AVPicture *)picture, (AVPicture *)picture, PIX_FMT_YUV420P, width, height); - - av_free(picture); - - return; + AVFrame *picture; + int width2 = width / 2; + + picture = avcodec_alloc_frame(); + + if (!picture) { + motion_log(LOG_ERR, 1, "%s: Could not alloc frame", __FUNCTION__); + return; + } + + picture->data[0] = img; + picture->data[1] = img + width * height; + picture->data[2] = picture->data[1] + (width * height) / 4; + picture->linesize[0] = width; + picture->linesize[1] = width2; + picture->linesize[2] = width2; + + /* We assume using 'PIX_FMT_YUV420P' always */ + avpicture_deinterlace((AVPicture *)picture, (AVPicture *)picture, PIX_FMT_YUV420P, width, height); + + av_free(picture); + + return; } /** ffmpeg_avcodec_log @@ -774,17 +783,17 @@ void ffmpeg_deinterlace(unsigned char *img, int width, int height) */ void ffmpeg_avcodec_log(void *ignoreme ATTRIBUTE_UNUSED, int errno_flag, const char *fmt, va_list vl) { - char buf[1024]; + char buf[1024]; - /* Do not log the message coming from avcodec if the debug_level is not set. */ - if (debug_level) { + /* Do not log the message coming from avcodec if the debug_level is not set. */ + if (debug_level) { - /* Flatten the message coming in from avcodec */ - vsnprintf(buf, sizeof(buf), fmt, vl); + /* Flatten the message coming in from avcodec */ + vsnprintf(buf, sizeof(buf), fmt, vl); - /* If the debug_level is correct then send the message to the motion logging routine. */ - motion_log(LOG_ERR, 0, "%s: %s - flag %d", __FUNCTION__, buf, errno_flag); - } + /* If the debug_level is correct then send the message to the motion logging routine. */ + motion_log(LOG_ERR, 0, "%s: %s - flag %d", __FUNCTION__, buf, errno_flag); + } } #endif /* HAVE_FFMPEG */ diff --git a/mmx.h b/mmx.h index 18a98b0..f1eee40 100644 --- a/mmx.h +++ b/mmx.h @@ -11,171 +11,171 @@ * values by ULL, lest they be truncated by the compiler) */ -typedef union { - long long q; /* Quadword (64-bit) value */ - unsigned long long uq; /* Unsigned Quadword */ - int d[2]; /* 2 Doubleword (32-bit) values */ - unsigned int ud[2]; /* 2 Unsigned Doubleword */ - short w[4]; /* 4 Word (16-bit) values */ - unsigned short uw[4]; /* 4 Unsigned Word */ - char b[8]; /* 8 Byte (8-bit) values */ - unsigned char ub[8]; /* 8 Unsigned Byte */ - float s[2]; /* Single-precision (32-bit) value */ -} mmx_t; /* On an 8-byte (64-bit) boundary */ - - -#define mmx_i2r(op,imm,reg) \ - __asm__ __volatile__ (#op " %0, %%" #reg \ - : /* nothing */ \ - : "i" (imm) ) - -#define mmx_m2r(op,mem,reg) \ - __asm__ __volatile__ (#op " %0, %%" #reg \ - : /* nothing */ \ - : "m" (mem)) - -#define mmx_r2m(op,reg,mem) \ - __asm__ __volatile__ (#op " %%" #reg ", %0" \ - : "=m" (mem) \ - : /* nothing */ ) - -#define mmx_r2r(op,regs,regd) \ - __asm__ __volatile__ (#op " %" #regs ", %" #regd) - - -#define emms() __asm__ __volatile__ ("emms") - -#define movd_m2r(var,reg) mmx_m2r (movd, var, reg) -#define movd_r2m(reg,var) mmx_r2m (movd, reg, var) -#define movd_r2r(regs,regd) mmx_r2r (movd, regs, regd) - -#define movq_m2r(var,reg) mmx_m2r (movq, var, reg) -#define movq_r2m(reg,var) mmx_r2m (movq, reg, var) -#define movq_r2r(regs,regd) mmx_r2r (movq, regs, regd) - -#define packssdw_m2r(var,reg) mmx_m2r (packssdw, var, reg) -#define packssdw_r2r(regs,regd) mmx_r2r (packssdw, regs, regd) -#define packsswb_m2r(var,reg) mmx_m2r (packsswb, var, reg) -#define packsswb_r2r(regs,regd) mmx_r2r (packsswb, regs, regd) - -#define packuswb_m2r(var,reg) mmx_m2r (packuswb, var, reg) -#define packuswb_r2r(regs,regd) mmx_r2r (packuswb, regs, regd) - -#define paddb_m2r(var,reg) mmx_m2r (paddb, var, reg) -#define paddb_r2r(regs,regd) mmx_r2r (paddb, regs, regd) -#define paddd_m2r(var,reg) mmx_m2r (paddd, var, reg) -#define paddd_r2r(regs,regd) mmx_r2r (paddd, regs, regd) -#define paddw_m2r(var,reg) mmx_m2r (paddw, var, reg) -#define paddw_r2r(regs,regd) mmx_r2r (paddw, regs, regd) - -#define paddsb_m2r(var,reg) mmx_m2r (paddsb, var, reg) -#define paddsb_r2r(regs,regd) mmx_r2r (paddsb, regs, regd) -#define paddsw_m2r(var,reg) mmx_m2r (paddsw, var, reg) -#define paddsw_r2r(regs,regd) mmx_r2r (paddsw, regs, regd) - -#define paddusb_m2r(var,reg) mmx_m2r (paddusb, var, reg) -#define paddusb_r2r(regs,regd) mmx_r2r (paddusb, regs, regd) -#define paddusw_m2r(var,reg) mmx_m2r (paddusw, var, reg) -#define paddusw_r2r(regs,regd) mmx_r2r (paddusw, regs, regd) - -#define pand_m2r(var,reg) mmx_m2r (pand, var, reg) -#define pand_r2r(regs,regd) mmx_r2r (pand, regs, regd) - -#define pandn_m2r(var,reg) mmx_m2r (pandn, var, reg) -#define pandn_r2r(regs,regd) mmx_r2r (pandn, regs, regd) - -#define pcmpeqb_m2r(var,reg) mmx_m2r (pcmpeqb, var, reg) -#define pcmpeqb_r2r(regs,regd) mmx_r2r (pcmpeqb, regs, regd) -#define pcmpeqd_m2r(var,reg) mmx_m2r (pcmpeqd, var, reg) -#define pcmpeqd_r2r(regs,regd) mmx_r2r (pcmpeqd, regs, regd) -#define pcmpeqw_m2r(var,reg) mmx_m2r (pcmpeqw, var, reg) -#define pcmpeqw_r2r(regs,regd) mmx_r2r (pcmpeqw, regs, regd) - -#define pcmpgtb_m2r(var,reg) mmx_m2r (pcmpgtb, var, reg) -#define pcmpgtb_r2r(regs,regd) mmx_r2r (pcmpgtb, regs, regd) -#define pcmpgtd_m2r(var,reg) mmx_m2r (pcmpgtd, var, reg) -#define pcmpgtd_r2r(regs,regd) mmx_r2r (pcmpgtd, regs, regd) -#define pcmpgtw_m2r(var,reg) mmx_m2r (pcmpgtw, var, reg) -#define pcmpgtw_r2r(regs,regd) mmx_r2r (pcmpgtw, regs, regd) - -#define pmaddwd_m2r(var,reg) mmx_m2r (pmaddwd, var, reg) -#define pmaddwd_r2r(regs,regd) mmx_r2r (pmaddwd, regs, regd) - -#define pmulhw_m2r(var,reg) mmx_m2r (pmulhw, var, reg) -#define pmulhw_r2r(regs,regd) mmx_r2r (pmulhw, regs, regd) - -#define pmullw_m2r(var,reg) mmx_m2r (pmullw, var, reg) -#define pmullw_r2r(regs,regd) mmx_r2r (pmullw, regs, regd) - -#define por_m2r(var,reg) mmx_m2r (por, var, reg) -#define por_r2r(regs,regd) mmx_r2r (por, regs, regd) - -#define pslld_i2r(imm,reg) mmx_i2r (pslld, imm, reg) -#define pslld_m2r(var,reg) mmx_m2r (pslld, var, reg) -#define pslld_r2r(regs,regd) mmx_r2r (pslld, regs, regd) -#define psllq_i2r(imm,reg) mmx_i2r (psllq, imm, reg) -#define psllq_m2r(var,reg) mmx_m2r (psllq, var, reg) -#define psllq_r2r(regs,regd) mmx_r2r (psllq, regs, regd) -#define psllw_i2r(imm,reg) mmx_i2r (psllw, imm, reg) -#define psllw_m2r(var,reg) mmx_m2r (psllw, var, reg) -#define psllw_r2r(regs,regd) mmx_r2r (psllw, regs, regd) - -#define psrad_i2r(imm,reg) mmx_i2r (psrad, imm, reg) -#define psrad_m2r(var,reg) mmx_m2r (psrad, var, reg) -#define psrad_r2r(regs,regd) mmx_r2r (psrad, regs, regd) -#define psraw_i2r(imm,reg) mmx_i2r (psraw, imm, reg) -#define psraw_m2r(var,reg) mmx_m2r (psraw, var, reg) -#define psraw_r2r(regs,regd) mmx_r2r (psraw, regs, regd) - -#define psrld_i2r(imm,reg) mmx_i2r (psrld, imm, reg) -#define psrld_m2r(var,reg) mmx_m2r (psrld, var, reg) -#define psrld_r2r(regs,regd) mmx_r2r (psrld, regs, regd) -#define psrlq_i2r(imm,reg) mmx_i2r (psrlq, imm, reg) -#define psrlq_m2r(var,reg) mmx_m2r (psrlq, var, reg) -#define psrlq_r2r(regs,regd) mmx_r2r (psrlq, regs, regd) -#define psrlw_i2r(imm,reg) mmx_i2r (psrlw, imm, reg) -#define psrlw_m2r(var,reg) mmx_m2r (psrlw, var, reg) -#define psrlw_r2r(regs,regd) mmx_r2r (psrlw, regs, regd) - -#define psubb_m2r(var,reg) mmx_m2r (psubb, var, reg) -#define psubb_r2r(regs,regd) mmx_r2r (psubb, regs, regd) -#define psubd_m2r(var,reg) mmx_m2r (psubd, var, reg) -#define psubd_r2r(regs,regd) mmx_r2r (psubd, regs, regd) -#define psubw_m2r(var,reg) mmx_m2r (psubw, var, reg) -#define psubw_r2r(regs,regd) mmx_r2r (psubw, regs, regd) - -#define psubsb_m2r(var,reg) mmx_m2r (psubsb, var, reg) -#define psubsb_r2r(regs,regd) mmx_r2r (psubsb, regs, regd) -#define psubsw_m2r(var,reg) mmx_m2r (psubsw, var, reg) -#define psubsw_r2r(regs,regd) mmx_r2r (psubsw, regs, regd) - -#define psubusb_m2r(var,reg) mmx_m2r (psubusb, var, reg) -#define psubusb_r2r(regs,regd) mmx_r2r (psubusb, regs, regd) -#define psubusw_m2r(var,reg) mmx_m2r (psubusw, var, reg) -#define psubusw_r2r(regs,regd) mmx_r2r (psubusw, regs, regd) - -#define punpckhbw_m2r(var,reg) mmx_m2r (punpckhbw, var, reg) -#define punpckhbw_r2r(regs,regd) mmx_r2r (punpckhbw, regs, regd) -#define punpckhdq_m2r(var,reg) mmx_m2r (punpckhdq, var, reg) -#define punpckhdq_r2r(regs,regd) mmx_r2r (punpckhdq, regs, regd) -#define punpckhwd_m2r(var,reg) mmx_m2r (punpckhwd, var, reg) -#define punpckhwd_r2r(regs,regd) mmx_r2r (punpckhwd, regs, regd) - -#define punpcklbw_m2r(var,reg) mmx_m2r (punpcklbw, var, reg) -#define punpcklbw_r2r(regs,regd) mmx_r2r (punpcklbw, regs, regd) -#define punpckldq_m2r(var,reg) mmx_m2r (punpckldq, var, reg) -#define punpckldq_r2r(regs,regd) mmx_r2r (punpckldq, regs, regd) -#define punpcklwd_m2r(var,reg) mmx_m2r (punpcklwd, var, reg) -#define punpcklwd_r2r(regs,regd) mmx_r2r (punpcklwd, regs, regd) - -#define pxor_m2r(var,reg) mmx_m2r (pxor, var, reg) -#define pxor_r2r(regs,regd) mmx_r2r (pxor, regs, regd) +typedef union { + long long q; /* Quadword (64-bit) value */ + unsigned long long uq; /* Unsigned Quadword */ + int d[2]; /* 2 Doubleword (32-bit) values */ + unsigned int ud[2]; /* 2 Unsigned Doubleword */ + short w[4]; /* 4 Word (16-bit) values */ + unsigned short uw[4]; /* 4 Unsigned Word */ + char b[8]; /* 8 Byte (8-bit) values */ + unsigned char ub[8]; /* 8 Unsigned Byte */ + float s[2]; /* Single-precision (32-bit) value */ +} mmx_t; /* On an 8-byte (64-bit) boundary */ + + +#define mmx_i2r(op,imm,reg) \ + __asm__ __volatile__ (#op " %0, %%" #reg \ + : /* nothing */ \ + : "i" (imm)) + +#define mmx_m2r(op,mem,reg) \ + __asm__ __volatile__ (#op " %0, %%" #reg \ + : /* nothing */ \ + : "m" (mem)) + +#define mmx_r2m(op,reg,mem) \ + __asm__ __volatile__ (#op " %%" #reg ", %0" \ + : "=m" (mem) \ + : /* nothing */ ) + +#define mmx_r2r(op,regs,regd) \ + __asm__ __volatile__ (#op " %" #regs ", %" #regd) + + +#define emms() __asm__ __volatile__ ("emms") + +#define movd_m2r(var,reg) mmx_m2r (movd, var, reg) +#define movd_r2m(reg,var) mmx_r2m (movd, reg, var) +#define movd_r2r(regs,regd) mmx_r2r (movd, regs, regd) + +#define movq_m2r(var,reg) mmx_m2r (movq, var, reg) +#define movq_r2m(reg,var) mmx_r2m (movq, reg, var) +#define movq_r2r(regs,regd) mmx_r2r (movq, regs, regd) + +#define packssdw_m2r(var,reg) mmx_m2r (packssdw, var, reg) +#define packssdw_r2r(regs,regd) mmx_r2r (packssdw, regs, regd) +#define packsswb_m2r(var,reg) mmx_m2r (packsswb, var, reg) +#define packsswb_r2r(regs,regd) mmx_r2r (packsswb, regs, regd) + +#define packuswb_m2r(var,reg) mmx_m2r (packuswb, var, reg) +#define packuswb_r2r(regs,regd) mmx_r2r (packuswb, regs, regd) + +#define paddb_m2r(var,reg) mmx_m2r (paddb, var, reg) +#define paddb_r2r(regs,regd) mmx_r2r (paddb, regs, regd) +#define paddd_m2r(var,reg) mmx_m2r (paddd, var, reg) +#define paddd_r2r(regs,regd) mmx_r2r (paddd, regs, regd) +#define paddw_m2r(var,reg) mmx_m2r (paddw, var, reg) +#define paddw_r2r(regs,regd) mmx_r2r (paddw, regs, regd) + +#define paddsb_m2r(var,reg) mmx_m2r (paddsb, var, reg) +#define paddsb_r2r(regs,regd) mmx_r2r (paddsb, regs, regd) +#define paddsw_m2r(var,reg) mmx_m2r (paddsw, var, reg) +#define paddsw_r2r(regs,regd) mmx_r2r (paddsw, regs, regd) + +#define paddusb_m2r(var,reg) mmx_m2r (paddusb, var, reg) +#define paddusb_r2r(regs,regd) mmx_r2r (paddusb, regs, regd) +#define paddusw_m2r(var,reg) mmx_m2r (paddusw, var, reg) +#define paddusw_r2r(regs,regd) mmx_r2r (paddusw, regs, regd) + +#define pand_m2r(var,reg) mmx_m2r (pand, var, reg) +#define pand_r2r(regs,regd) mmx_r2r (pand, regs, regd) + +#define pandn_m2r(var,reg) mmx_m2r (pandn, var, reg) +#define pandn_r2r(regs,regd) mmx_r2r (pandn, regs, regd) + +#define pcmpeqb_m2r(var,reg) mmx_m2r (pcmpeqb, var, reg) +#define pcmpeqb_r2r(regs,regd) mmx_r2r (pcmpeqb, regs, regd) +#define pcmpeqd_m2r(var,reg) mmx_m2r (pcmpeqd, var, reg) +#define pcmpeqd_r2r(regs,regd) mmx_r2r (pcmpeqd, regs, regd) +#define pcmpeqw_m2r(var,reg) mmx_m2r (pcmpeqw, var, reg) +#define pcmpeqw_r2r(regs,regd) mmx_r2r (pcmpeqw, regs, regd) + +#define pcmpgtb_m2r(var,reg) mmx_m2r (pcmpgtb, var, reg) +#define pcmpgtb_r2r(regs,regd) mmx_r2r (pcmpgtb, regs, regd) +#define pcmpgtd_m2r(var,reg) mmx_m2r (pcmpgtd, var, reg) +#define pcmpgtd_r2r(regs,regd) mmx_r2r (pcmpgtd, regs, regd) +#define pcmpgtw_m2r(var,reg) mmx_m2r (pcmpgtw, var, reg) +#define pcmpgtw_r2r(regs,regd) mmx_r2r (pcmpgtw, regs, regd) + +#define pmaddwd_m2r(var,reg) mmx_m2r (pmaddwd, var, reg) +#define pmaddwd_r2r(regs,regd) mmx_r2r (pmaddwd, regs, regd) + +#define pmulhw_m2r(var,reg) mmx_m2r (pmulhw, var, reg) +#define pmulhw_r2r(regs,regd) mmx_r2r (pmulhw, regs, regd) + +#define pmullw_m2r(var,reg) mmx_m2r (pmullw, var, reg) +#define pmullw_r2r(regs,regd) mmx_r2r (pmullw, regs, regd) + +#define por_m2r(var,reg) mmx_m2r (por, var, reg) +#define por_r2r(regs,regd) mmx_r2r (por, regs, regd) + +#define pslld_i2r(imm,reg) mmx_i2r (pslld, imm, reg) +#define pslld_m2r(var,reg) mmx_m2r (pslld, var, reg) +#define pslld_r2r(regs,regd) mmx_r2r (pslld, regs, regd) +#define psllq_i2r(imm,reg) mmx_i2r (psllq, imm, reg) +#define psllq_m2r(var,reg) mmx_m2r (psllq, var, reg) +#define psllq_r2r(regs,regd) mmx_r2r (psllq, regs, regd) +#define psllw_i2r(imm,reg) mmx_i2r (psllw, imm, reg) +#define psllw_m2r(var,reg) mmx_m2r (psllw, var, reg) +#define psllw_r2r(regs,regd) mmx_r2r (psllw, regs, regd) + +#define psrad_i2r(imm,reg) mmx_i2r (psrad, imm, reg) +#define psrad_m2r(var,reg) mmx_m2r (psrad, var, reg) +#define psrad_r2r(regs,regd) mmx_r2r (psrad, regs, regd) +#define psraw_i2r(imm,reg) mmx_i2r (psraw, imm, reg) +#define psraw_m2r(var,reg) mmx_m2r (psraw, var, reg) +#define psraw_r2r(regs,regd) mmx_r2r (psraw, regs, regd) + +#define psrld_i2r(imm,reg) mmx_i2r (psrld, imm, reg) +#define psrld_m2r(var,reg) mmx_m2r (psrld, var, reg) +#define psrld_r2r(regs,regd) mmx_r2r (psrld, regs, regd) +#define psrlq_i2r(imm,reg) mmx_i2r (psrlq, imm, reg) +#define psrlq_m2r(var,reg) mmx_m2r (psrlq, var, reg) +#define psrlq_r2r(regs,regd) mmx_r2r (psrlq, regs, regd) +#define psrlw_i2r(imm,reg) mmx_i2r (psrlw, imm, reg) +#define psrlw_m2r(var,reg) mmx_m2r (psrlw, var, reg) +#define psrlw_r2r(regs,regd) mmx_r2r (psrlw, regs, regd) + +#define psubb_m2r(var,reg) mmx_m2r (psubb, var, reg) +#define psubb_r2r(regs,regd) mmx_r2r (psubb, regs, regd) +#define psubd_m2r(var,reg) mmx_m2r (psubd, var, reg) +#define psubd_r2r(regs,regd) mmx_r2r (psubd, regs, regd) +#define psubw_m2r(var,reg) mmx_m2r (psubw, var, reg) +#define psubw_r2r(regs,regd) mmx_r2r (psubw, regs, regd) + +#define psubsb_m2r(var,reg) mmx_m2r (psubsb, var, reg) +#define psubsb_r2r(regs,regd) mmx_r2r (psubsb, regs, regd) +#define psubsw_m2r(var,reg) mmx_m2r (psubsw, var, reg) +#define psubsw_r2r(regs,regd) mmx_r2r (psubsw, regs, regd) + +#define psubusb_m2r(var,reg) mmx_m2r (psubusb, var, reg) +#define psubusb_r2r(regs,regd) mmx_r2r (psubusb, regs, regd) +#define psubusw_m2r(var,reg) mmx_m2r (psubusw, var, reg) +#define psubusw_r2r(regs,regd) mmx_r2r (psubusw, regs, regd) + +#define punpckhbw_m2r(var,reg) mmx_m2r (punpckhbw, var, reg) +#define punpckhbw_r2r(regs,regd) mmx_r2r (punpckhbw, regs, regd) +#define punpckhdq_m2r(var,reg) mmx_m2r (punpckhdq, var, reg) +#define punpckhdq_r2r(regs,regd) mmx_r2r (punpckhdq, regs, regd) +#define punpckhwd_m2r(var,reg) mmx_m2r (punpckhwd, var, reg) +#define punpckhwd_r2r(regs,regd) mmx_r2r (punpckhwd, regs, regd) + +#define punpcklbw_m2r(var,reg) mmx_m2r (punpcklbw, var, reg) +#define punpcklbw_r2r(regs,regd) mmx_r2r (punpcklbw, regs, regd) +#define punpckldq_m2r(var,reg) mmx_m2r (punpckldq, var, reg) +#define punpckldq_r2r(regs,regd) mmx_r2r (punpckldq, regs, regd) +#define punpcklwd_m2r(var,reg) mmx_m2r (punpcklwd, var, reg) +#define punpcklwd_r2r(regs,regd) mmx_r2r (punpcklwd, regs, regd) + +#define pxor_m2r(var,reg) mmx_m2r (pxor, var, reg) +#define pxor_r2r(regs,regd) mmx_r2r (pxor, regs, regd) /* 3DNOW extensions */ -#define pavgusb_m2r(var,reg) mmx_m2r (pavgusb, var, reg) -#define pavgusb_r2r(regs,regd) mmx_r2r (pavgusb, regs, regd) +#define pavgusb_m2r(var,reg) mmx_m2r (pavgusb, var, reg) +#define pavgusb_r2r(regs,regd) mmx_r2r (pavgusb, regs, regd) /* AMD MMX extensions - also available in intel SSE */ @@ -188,80 +188,80 @@ typedef union { #define mmx_r2ri(op,regs,regd,imm) \ __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \ : /* nothing */ \ - : "X" (imm) ) + : "X" (imm)) -#define mmx_fetch(mem,hint) \ - __asm__ __volatile__ ("prefetch" #hint " %0" \ - : /* nothing */ \ - : "X" (mem)) +#define mmx_fetch(mem,hint) \ + __asm__ __volatile__ ("prefetch" #hint " %0" \ + : /* nothing */ \ + : "X" (mem)) -#define maskmovq(regs,maskreg) mmx_r2ri (maskmovq, regs, maskreg) +#define maskmovq(regs,maskreg) mmx_r2ri (maskmovq, regs, maskreg) -#define movntq_r2m(mmreg,var) mmx_r2m (movntq, mmreg, var) +#define movntq_r2m(mmreg,var) mmx_r2m (movntq, mmreg, var) -#define pavgb_m2r(var,reg) mmx_m2r (pavgb, var, reg) -#define pavgb_r2r(regs,regd) mmx_r2r (pavgb, regs, regd) -#define pavgw_m2r(var,reg) mmx_m2r (pavgw, var, reg) -#define pavgw_r2r(regs,regd) mmx_r2r (pavgw, regs, regd) +#define pavgb_m2r(var,reg) mmx_m2r (pavgb, var, reg) +#define pavgb_r2r(regs,regd) mmx_r2r (pavgb, regs, regd) +#define pavgw_m2r(var,reg) mmx_m2r (pavgw, var, reg) +#define pavgw_r2r(regs,regd) mmx_r2r (pavgw, regs, regd) -#define pextrw_r2r(mmreg,reg,imm) mmx_r2ri (pextrw, mmreg, reg, imm) +#define pextrw_r2r(mmreg,reg,imm) mmx_r2ri (pextrw, mmreg, reg, imm) -#define pinsrw_r2r(reg,mmreg,imm) mmx_r2ri (pinsrw, reg, mmreg, imm) +#define pinsrw_r2r(reg,mmreg,imm) mmx_r2ri (pinsrw, reg, mmreg, imm) -#define pmaxsw_m2r(var,reg) mmx_m2r (pmaxsw, var, reg) -#define pmaxsw_r2r(regs,regd) mmx_r2r (pmaxsw, regs, regd) +#define pmaxsw_m2r(var,reg) mmx_m2r (pmaxsw, var, reg) +#define pmaxsw_r2r(regs,regd) mmx_r2r (pmaxsw, regs, regd) -#define pmaxub_m2r(var,reg) mmx_m2r (pmaxub, var, reg) -#define pmaxub_r2r(regs,regd) mmx_r2r (pmaxub, regs, regd) +#define pmaxub_m2r(var,reg) mmx_m2r (pmaxub, var, reg) +#define pmaxub_r2r(regs,regd) mmx_r2r (pmaxub, regs, regd) -#define pminsw_m2r(var,reg) mmx_m2r (pminsw, var, reg) -#define pminsw_r2r(regs,regd) mmx_r2r (pminsw, regs, regd) +#define pminsw_m2r(var,reg) mmx_m2r (pminsw, var, reg) +#define pminsw_r2r(regs,regd) mmx_r2r (pminsw, regs, regd) -#define pminub_m2r(var,reg) mmx_m2r (pminub, var, reg) -#define pminub_r2r(regs,regd) mmx_r2r (pminub, regs, regd) +#define pminub_m2r(var,reg) mmx_m2r (pminub, var, reg) +#define pminub_r2r(regs,regd) mmx_r2r (pminub, regs, regd) -#define pmovmskb(mmreg,reg) \ - __asm__ __volatile__ ("movmskps %" #mmreg ", %" #reg) +#define pmovmskb(mmreg,reg) \ + __asm__ __volatile__ ("movmskps %" #mmreg ", %" #reg) -#define pmulhuw_m2r(var,reg) mmx_m2r (pmulhuw, var, reg) -#define pmulhuw_r2r(regs,regd) mmx_r2r (pmulhuw, regs, regd) +#define pmulhuw_m2r(var,reg) mmx_m2r (pmulhuw, var, reg) +#define pmulhuw_r2r(regs,regd) mmx_r2r (pmulhuw, regs, regd) -#define prefetcht0(mem) mmx_fetch (mem, t0) -#define prefetcht1(mem) mmx_fetch (mem, t1) -#define prefetcht2(mem) mmx_fetch (mem, t2) -#define prefetchnta(mem) mmx_fetch (mem, nta) +#define prefetcht0(mem) mmx_fetch (mem, t0) +#define prefetcht1(mem) mmx_fetch (mem, t1) +#define prefetcht2(mem) mmx_fetch (mem, t2) +#define prefetchnta(mem) mmx_fetch (mem, nta) -#define psadbw_m2r(var,reg) mmx_m2r (psadbw, var, reg) -#define psadbw_r2r(regs,regd) mmx_r2r (psadbw, regs, regd) +#define psadbw_m2r(var,reg) mmx_m2r (psadbw, var, reg) +#define psadbw_r2r(regs,regd) mmx_r2r (psadbw, regs, regd) -#define pshufw_m2r(var,reg,imm) mmx_m2ri(pshufw, var, reg, imm) -#define pshufw_r2r(regs,regd,imm) mmx_r2ri(pshufw, regs, regd, imm) +#define pshufw_m2r(var,reg,imm) mmx_m2ri(pshufw, var, reg, imm) +#define pshufw_r2r(regs,regd,imm) mmx_r2ri(pshufw, regs, regd, imm) -#define sfence() __asm__ __volatile__ ("sfence\n\t") +#define sfence() __asm__ __volatile__ ("sfence\n\t") /* SSE2 */ -#define pshufhw_m2r(var,reg,imm) mmx_m2ri(pshufhw, var, reg, imm) -#define pshufhw_r2r(regs,regd,imm) mmx_r2ri(pshufhw, regs, regd, imm) -#define pshuflw_m2r(var,reg,imm) mmx_m2ri(pshuflw, var, reg, imm) -#define pshuflw_r2r(regs,regd,imm) mmx_r2ri(pshuflw, regs, regd, imm) +#define pshufhw_m2r(var,reg,imm) mmx_m2ri(pshufhw, var, reg, imm) +#define pshufhw_r2r(regs,regd,imm) mmx_r2ri(pshufhw, regs, regd, imm) +#define pshuflw_m2r(var,reg,imm) mmx_m2ri(pshuflw, var, reg, imm) +#define pshuflw_r2r(regs,regd,imm) mmx_r2ri(pshuflw, regs, regd, imm) -#define pshufd_r2r(regs,regd,imm) mmx_r2ri(pshufd, regs, regd, imm) +#define pshufd_r2r(regs,regd,imm) mmx_r2ri(pshufd, regs, regd, imm) -#define movdqa_m2r(var,reg) mmx_m2r (movdqa, var, reg) -#define movdqa_r2m(reg,var) mmx_r2m (movdqa, reg, var) -#define movdqa_r2r(regs,regd) mmx_r2r (movdqa, regs, regd) -#define movdqu_m2r(var,reg) mmx_m2r (movdqu, var, reg) -#define movdqu_r2m(reg,var) mmx_r2m (movdqu, reg, var) -#define movdqu_r2r(regs,regd) mmx_r2r (movdqu, regs, regd) +#define movdqa_m2r(var,reg) mmx_m2r (movdqa, var, reg) +#define movdqa_r2m(reg,var) mmx_r2m (movdqa, reg, var) +#define movdqa_r2r(regs,regd) mmx_r2r (movdqa, regs, regd) +#define movdqu_m2r(var,reg) mmx_m2r (movdqu, var, reg) +#define movdqu_r2m(reg,var) mmx_r2m (movdqu, reg, var) +#define movdqu_r2r(regs,regd) mmx_r2r (movdqu, regs, regd) -#define pmullw_r2m(reg,var) mmx_r2m (pmullw, reg, var) +#define pmullw_r2m(reg,var) mmx_r2m (pmullw, reg, var) -#define pslldq_i2r(imm,reg) mmx_i2r (pslldq, imm, reg) -#define psrldq_i2r(imm,reg) mmx_i2r (psrldq, imm, reg) +#define pslldq_i2r(imm,reg) mmx_i2r (pslldq, imm, reg) +#define psrldq_i2r(imm,reg) mmx_i2r (psrldq, imm, reg) -#define punpcklqdq_r2r(regs,regd) mmx_r2r (punpcklqdq, regs, regd) -#define punpckhqdq_r2r(regs,regd) mmx_r2r (punpckhqdq, regs, regd) +#define punpcklqdq_r2r(regs,regd) mmx_r2r (punpcklqdq, regs, regd) +#define punpckhqdq_r2r(regs,regd) mmx_r2r (punpckhqdq, regs, regd) #endif /* I386MMX_H */ diff --git a/motion.c b/motion.c index 96bae50..d031c2d 100644 --- a/motion.c +++ b/motion.c @@ -1,9 +1,9 @@ -/* motion.c +/* motion.c * - * Detect changes in a video stream. - * Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org) - * This software is distributed under the GNU public license version 2 - * See also the file 'COPYING'. + * Detect changes in a video stream. + * Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org) + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. * */ #include "ffmpeg.h" @@ -92,50 +92,51 @@ unsigned short int restart = 0; */ static void image_ring_resize(struct context *cnt, int new_size) { - /* Only resize if : - * Not in an event and - * decreasing at last position in new buffer - * increasing at last position in old buffer - * e.g. at end of smallest buffer */ - if (cnt->event_nr != cnt->prev_event) { - int smallest; - if (new_size < cnt->imgs.image_ring_size) { /* Decreasing */ - smallest = new_size; - } else { /* Increasing */ - smallest = cnt->imgs.image_ring_size; - } - if (cnt->imgs.image_ring_in == smallest - 1 || smallest == 0) { - motion_log(LOG_INFO, 0, "%s: Resizing pre_capture buffer to %d items", - __FUNCTION__, new_size); - - /* Create memory for new ring buffer */ - struct image_data *tmp; - tmp = mymalloc(new_size * sizeof(struct image_data)); - - /* Copy all information from old to new - * Smallest is 0 at initial init */ - if (smallest > 0) { - memcpy(tmp, cnt->imgs.image_ring, sizeof(struct image_data) * smallest); - } - - /* In the new buffers, allocate image memory */ - { - int i; - for(i = smallest; i < new_size; i++) { - tmp[i].image = mymalloc(cnt->imgs.size); - memset(tmp[i].image, 0x80, cnt->imgs.size); /* initialize to grey */ - } - } - - /* Free the old ring */ - free(cnt->imgs.image_ring); - - /* Point to the new ring */ - cnt->imgs.image_ring = tmp; - - cnt->imgs.image_ring_size = new_size; - } - } + /* Only resize if : + * Not in an event and + * decreasing at last position in new buffer + * increasing at last position in old buffer + * e.g. at end of smallest buffer */ + if (cnt->event_nr != cnt->prev_event) { + int smallest; + + if (new_size < cnt->imgs.image_ring_size) /* Decreasing */ + smallest = new_size; + else /* Increasing */ + smallest = cnt->imgs.image_ring_size; + + if (cnt->imgs.image_ring_in == smallest - 1 || smallest == 0) { + motion_log(LOG_INFO, 0, "%s: Resizing pre_capture buffer to %d items", + __FUNCTION__, new_size); + + /* Create memory for new ring buffer */ + struct image_data *tmp; + tmp = mymalloc(new_size * sizeof(struct image_data)); + + /* Copy all information from old to new + * Smallest is 0 at initial init */ + if (smallest > 0) + memcpy(tmp, cnt->imgs.image_ring, sizeof(struct image_data) * smallest); + + + /* In the new buffers, allocate image memory */ + { + int i; + for(i = smallest; i < new_size; i++) { + tmp[i].image = mymalloc(cnt->imgs.size); + memset(tmp[i].image, 0x80, cnt->imgs.size); /* initialize to grey */ + } + } + + /* Free the old ring */ + free(cnt->imgs.image_ring); + + /* Point to the new ring */ + cnt->imgs.image_ring = tmp; + + cnt->imgs.image_ring_size = new_size; + } + } } /** @@ -151,22 +152,22 @@ static void image_ring_resize(struct context *cnt, int new_size) */ static void image_ring_destroy(struct context *cnt) { - unsigned short int i; - - /* Exit if don't have any ring */ - if (cnt->imgs.image_ring == NULL) - return; - - /* Free all image buffers */ - for (i = 0; i < cnt->imgs.image_ring_size; i++) { - free(cnt->imgs.image_ring[i].image); - } - - /* Free the ring */ - free(cnt->imgs.image_ring); - - cnt->imgs.image_ring = NULL; - cnt->imgs.image_ring_size = 0; + unsigned short int i; + + /* Exit if don't have any ring */ + if (cnt->imgs.image_ring == NULL) + return; + + /* Free all image buffers */ + for (i = 0; i < cnt->imgs.image_ring_size; i++) + free(cnt->imgs.image_ring[i].image); + + + /* Free the ring */ + free(cnt->imgs.image_ring); + + cnt->imgs.image_ring = NULL; + cnt->imgs.image_ring_size = 0; } /** @@ -183,26 +184,26 @@ static void image_ring_destroy(struct context *cnt) */ static void image_save_as_preview(struct context *cnt, struct image_data *img) { - void * image; - /* Save preview image pointer */ - image = cnt->imgs.preview_image.image; - /* Copy all info */ - memcpy(&cnt->imgs.preview_image.image, img, sizeof(struct image_data)); - /* restore image pointer */ - cnt->imgs.preview_image.image = image; - - /* Copy image */ - memcpy(cnt->imgs.preview_image.image, img->image, cnt->imgs.size); - - /* If we set output_all to yes and during the event - * there is no image with motion, diffs is 0, we are not going to save the preview event */ - if (cnt->imgs.preview_image.diffs == 0) - cnt->imgs.preview_image.diffs = 1; - - /* If we have locate on it is already done */ - if (cnt->locate_motion == LOCATE_PREVIEW) { - alg_draw_red_location(&img->location, &cnt->imgs, cnt->imgs.width, cnt->imgs.preview_image.image, LOCATE_NORMAL); - } + void * image; + /* Save preview image pointer */ + image = cnt->imgs.preview_image.image; + /* Copy all info */ + memcpy(&cnt->imgs.preview_image.image, img, sizeof(struct image_data)); + /* restore image pointer */ + cnt->imgs.preview_image.image = image; + + /* Copy image */ + memcpy(cnt->imgs.preview_image.image, img->image, cnt->imgs.size); + + /* If we set output_all to yes and during the event + * there is no image with motion, diffs is 0, we are not going to save the preview event */ + if (cnt->imgs.preview_image.diffs == 0) + cnt->imgs.preview_image.diffs = 1; + + /* If we have locate on it is already done */ + if (cnt->locate_motion == LOCATE_PREVIEW) + alg_draw_red_location(&img->location, &cnt->imgs, cnt->imgs.width, cnt->imgs.preview_image.image, LOCATE_NORMAL); + } /** @@ -219,23 +220,23 @@ static void image_save_as_preview(struct context *cnt, struct image_data *img) */ static void context_init (struct context *cnt) { - /* - * We first clear the entire structure to zero, then fill in any - * values which have non-zero default values. Note that this - * assumes that a NULL address pointer has a value of binary 0 - * (this is also assumed at other places within the code, i.e. - * there are instances of "if (ptr)"). Just for possible future - * changes to this assumption, any pointers which are intended - * to be initialised to NULL are listed within a comment. - */ - - memset(cnt, 0, sizeof(struct context)); - cnt->noise = 255; - cnt->lastrate = 25; - - memcpy(&cnt->track, &track_template, sizeof(struct trackoptions)); - cnt->pipe = -1; - cnt->mpipe = -1; + /* + * We first clear the entire structure to zero, then fill in any + * values which have non-zero default values. Note that this + * assumes that a NULL address pointer has a value of binary 0 + * (this is also assumed at other places within the code, i.e. + * there are instances of "if (ptr)"). Just for possible future + * changes to this assumption, any pointers which are intended + * to be initialised to NULL are listed within a comment. + */ + + memset(cnt, 0, sizeof(struct context)); + cnt->noise = 255; + cnt->lastrate = 25; + + memcpy(&cnt->track, &track_template, sizeof(struct trackoptions)); + cnt->pipe = -1; + cnt->mpipe = -1; } @@ -253,21 +254,21 @@ static void context_init (struct context *cnt) */ static void context_destroy(struct context *cnt) { - unsigned short int j; - - /* Free memory allocated for config parameters */ - for (j = 0; config_params[j].param_name != NULL; j++) { - if (config_params[j].copy == copy_string) { - void **val; - val = (void *)((char *)cnt+(int)config_params[j].conf_value); - if (*val) { - free(*val); - *val = NULL; - } - } - } - - free(cnt); + unsigned short int j; + + /* Free memory allocated for config parameters */ + for (j = 0; config_params[j].param_name != NULL; j++) { + if (config_params[j].copy == copy_string) { + void **val; + val = (void *)((char *)cnt+(int)config_params[j].conf_value); + if (*val) { + free(*val); + *val = NULL; + } + } + } + + free(cnt); } /** @@ -277,60 +278,60 @@ static void context_destroy(struct context *cnt) */ static void sig_handler(int signo) { - short int i; - - switch(signo) { - case SIGALRM: - /* Somebody (maybe we ourself) wants us to make a snapshot - * This feature triggers snapshots on ALL threads that have - * snapshot_interval different from 0. - */ - if (cnt_list) { - i = -1; - while (cnt_list[++i]) { - if (cnt_list[i]->conf.snapshot_interval) { - cnt_list[i]->snapshot = 1; - } - } - } - break; - case SIGUSR1: - /* Ouch! We have been hit from the outside! Someone wants us to - make a movie! */ - if (cnt_list) { - i = -1; - while (cnt_list[++i]) - cnt_list[i]->makemovie = 1; - } - break; - case SIGHUP: - restart = 1; - /* Fall through, as the value of 'restart' is the only difference - * between SIGHUP and the ones below. - */ - case SIGINT: - case SIGQUIT: - case SIGTERM: - /* Somebody wants us to quit! We should better finish the actual - movie and end up! */ - if (cnt_list) { - i = -1; - while (cnt_list[++i]) { - cnt_list[i]->makemovie = 1; - cnt_list[i]->finish = 1; - /* don't restart thread when it ends, - * all threads restarts if global restart is set - */ - cnt_list[i]->restart = 0; - } - } - /* Set flag we want to quit main check threads loop - * if restart is set (above) we start up again */ - finish = 1; - break; - case SIGSEGV: - exit(0); - } + short int i; + + switch(signo) { + case SIGALRM: + /* Somebody (maybe we ourself) wants us to make a snapshot + * This feature triggers snapshots on ALL threads that have + * snapshot_interval different from 0. + */ + if (cnt_list) { + i = -1; + while (cnt_list[++i]) { + if (cnt_list[i]->conf.snapshot_interval) + cnt_list[i]->snapshot = 1; + + } + } + break; + case SIGUSR1: + /* Ouch! We have been hit from the outside! Someone wants us to + make a movie! */ + if (cnt_list) { + i = -1; + while (cnt_list[++i]) + cnt_list[i]->makemovie = 1; + } + break; + case SIGHUP: + restart = 1; + /* Fall through, as the value of 'restart' is the only difference + * between SIGHUP and the ones below. + */ + case SIGINT: + case SIGQUIT: + case SIGTERM: + /* Somebody wants us to quit! We should better finish the actual + movie and end up! */ + if (cnt_list) { + i = -1; + while (cnt_list[++i]) { + cnt_list[i]->makemovie = 1; + cnt_list[i]->finish = 1; + /* don't restart thread when it ends, + * all threads restarts if global restart is set + */ + cnt_list[i]->restart = 0; + } + } + /* Set flag we want to quit main check threads loop + * if restart is set (above) we start up again */ + finish = 1; + break; + case SIGSEGV: + exit(0); + } } /** @@ -342,9 +343,9 @@ static void sig_handler(int signo) static void sigchild_handler(int signo ATTRIBUTE_UNUSED) { #ifdef WNOHANG - while (waitpid(-1, NULL, WNOHANG) > 0) {}; + while (waitpid(-1, NULL, WNOHANG) > 0) {}; #endif /* WNOHANG */ - return; + return; } /** @@ -355,12 +356,12 @@ static void sigchild_handler(int signo ATTRIBUTE_UNUSED) */ static void motion_remove_pid(void) { - if ((cnt_list[0]->daemon) && (cnt_list[0]->conf.pid_file) && (restart == 0)) { - if (!unlink(cnt_list[0]->conf.pid_file)) - motion_log(LOG_INFO, 0, "%s: Removed process id file (pid file).", __FUNCTION__); - else - motion_log(LOG_INFO, 1, "%s: Error removing pid file", __FUNCTION__); - } + if ((cnt_list[0]->daemon) && (cnt_list[0]->conf.pid_file) && (restart == 0)) { + if (!unlink(cnt_list[0]->conf.pid_file)) + motion_log(LOG_INFO, 0, "%s: Removed process id file (pid file).", __FUNCTION__); + else + motion_log(LOG_INFO, 1, "%s: Error removing pid file", __FUNCTION__); + } } /** @@ -377,86 +378,86 @@ static void motion_remove_pid(void) */ static void motion_detected(struct context *cnt, int dev, struct image_data *img) { - struct config *conf = &cnt->conf; - struct images *imgs = &cnt->imgs; - struct coord *location = &img->location; - - /* Draw location */ - if (cnt->locate_motion == LOCATE_ON) - alg_draw_location(location, imgs, imgs->width, img->image, LOCATE_BOTH); - else if (cnt->locate_motion == LOCATE_REDBOX) - alg_draw_red_location(location, imgs, imgs->width, img->image, LOCATE_BOTH); - else if (cnt->locate_motion == LOCATE_CENTER) - alg_draw_location(location, imgs, imgs->width, img->image, LOCATE_CENTER); - else if (cnt->locate_motion == LOCATE_REDCROSS) - alg_draw_red_location(location, imgs, imgs->width, img->image, LOCATE_CENTER); - - /* Calculate how centric motion is if configured preview center*/ - if (cnt->new_img & NEWIMG_CENTER) { - unsigned int distX = abs((imgs->width/2) - location->x); - unsigned int distY = abs((imgs->height/2) - location->y); - - img->cent_dist = distX*distX + distY*distY; - } - - - /* Do things only if we have got minimum_motion_frames */ - if (img->flags & IMAGE_TRIGGER) { - /* Take action if this is a new event and we have a trigger image */ - if (cnt->event_nr != cnt->prev_event) { - /* Reset prev_event number to current event and save event time - * in both time_t and struct tm format. - */ - cnt->prev_event = cnt->event_nr; - cnt->eventtime = img->timestamp; - localtime_r(&cnt->eventtime, cnt->eventtime_tm); - - /* Since this is a new event we create the event_text_string used for - * the %C conversion specifier. We may already need it for - * on_motion_detected_commend so it must be done now. - */ - mystrftime(cnt, cnt->text_event_string, sizeof(cnt->text_event_string), - cnt->conf.text_event, cnt->eventtime_tm, NULL, 0); - - /* EVENT_FIRSTMOTION triggers on_event_start_command and event_ffmpeg_newfile */ - event(cnt, EVENT_FIRSTMOTION, img->image, NULL, NULL, &img->timestamp_tm); - - if (debug_level >= CAMERA_INFO) - motion_log(-1, 0, "%s: Motion detected - starting event %d", - __FUNCTION__, cnt->event_nr); - - /* always save first motion frame as preview-shot, may be changed to an other one later */ - if (cnt->new_img & (NEWIMG_FIRST | NEWIMG_BEST | NEWIMG_CENTER)) { - image_save_as_preview(cnt, img); - } - } - - /* EVENT_MOTION triggers event_beep and on_motion_detected_command */ - event(cnt, EVENT_MOTION, NULL, NULL, NULL, &img->timestamp_tm); - } - - /* Limit framerate */ - if (img->shot < conf->frame_limit) { - /* If config option stream_motion is enabled, send the latest motion detected image - * to the stream but only if it is not the first shot within a second. This is to - * avoid double frames since we already have sent a frame to the stream. - * We also disable this in setup_mode. - */ - if (conf->stream_motion && !conf->setup_mode && img->shot != 1) { - event(cnt, EVENT_STREAM, img->image, NULL, NULL, &img->timestamp_tm); - } - - /* Save motion jpeg, if configured */ - /* Output the image_out (motion) picture. */ - if (conf->motion_img) { - event(cnt, EVENT_IMAGEM_DETECTED, NULL, NULL, NULL, &img->timestamp_tm); - } - } - - /* if track enabled and auto track on */ - if ((cnt->track.type) && (cnt->track.active)){ - cnt->moved = track_move(cnt, dev, location, imgs, 0); - } + struct config *conf = &cnt->conf; + struct images *imgs = &cnt->imgs; + struct coord *location = &img->location; + + /* Draw location */ + if (cnt->locate_motion == LOCATE_ON) + alg_draw_location(location, imgs, imgs->width, img->image, LOCATE_BOTH); + else if (cnt->locate_motion == LOCATE_REDBOX) + alg_draw_red_location(location, imgs, imgs->width, img->image, LOCATE_BOTH); + else if (cnt->locate_motion == LOCATE_CENTER) + alg_draw_location(location, imgs, imgs->width, img->image, LOCATE_CENTER); + else if (cnt->locate_motion == LOCATE_REDCROSS) + alg_draw_red_location(location, imgs, imgs->width, img->image, LOCATE_CENTER); + + /* Calculate how centric motion is if configured preview center*/ + if (cnt->new_img & NEWIMG_CENTER) { + unsigned int distX = abs((imgs->width / 2) - location->x); + unsigned int distY = abs((imgs->height / 2) - location->y); + + img->cent_dist = distX * distX + distY * distY; + } + + + /* Do things only if we have got minimum_motion_frames */ + if (img->flags & IMAGE_TRIGGER) { + /* Take action if this is a new event and we have a trigger image */ + if (cnt->event_nr != cnt->prev_event) { + /* Reset prev_event number to current event and save event time + * in both time_t and struct tm format. + */ + cnt->prev_event = cnt->event_nr; + cnt->eventtime = img->timestamp; + localtime_r(&cnt->eventtime, cnt->eventtime_tm); + + /* Since this is a new event we create the event_text_string used for + * the %C conversion specifier. We may already need it for + * on_motion_detected_commend so it must be done now. + */ + mystrftime(cnt, cnt->text_event_string, sizeof(cnt->text_event_string), + cnt->conf.text_event, cnt->eventtime_tm, NULL, 0); + + /* EVENT_FIRSTMOTION triggers on_event_start_command and event_ffmpeg_newfile */ + event(cnt, EVENT_FIRSTMOTION, img->image, NULL, NULL, &img->timestamp_tm); + + if (debug_level >= CAMERA_INFO) + motion_log(-1, 0, "%s: Motion detected - starting event %d", + __FUNCTION__, cnt->event_nr); + + /* always save first motion frame as preview-shot, may be changed to an other one later */ + if (cnt->new_img & (NEWIMG_FIRST | NEWIMG_BEST | NEWIMG_CENTER)) + image_save_as_preview(cnt, img); + + } + + /* EVENT_MOTION triggers event_beep and on_motion_detected_command */ + event(cnt, EVENT_MOTION, NULL, NULL, NULL, &img->timestamp_tm); + } + + /* Limit framerate */ + if (img->shot < conf->frame_limit) { + /* If config option stream_motion is enabled, send the latest motion detected image + * to the stream but only if it is not the first shot within a second. This is to + * avoid double frames since we already have sent a frame to the stream. + * We also disable this in setup_mode. + */ + if (conf->stream_motion && !conf->setup_mode && img->shot != 1) + event(cnt, EVENT_STREAM, img->image, NULL, NULL, &img->timestamp_tm); + + + /* Save motion jpeg, if configured */ + /* Output the image_out (motion) picture. */ + if (conf->motion_img) + event(cnt, EVENT_IMAGEM_DETECTED, NULL, NULL, NULL, &img->timestamp_tm); + + } + + /* if track enabled and auto track on */ + if (cnt->track.type && cnt->track.active) + cnt->moved = track_move(cnt, dev, location, imgs, 0); + } /** @@ -473,123 +474,122 @@ static void motion_detected(struct context *cnt, int dev, struct image_data *img #define IMAGE_BUFFER_FLUSH ((unsigned int)-1) static void process_image_ring(struct context *cnt, unsigned int max_images) { - /* we are going to send an event, in the events there is still - * some code that use cnt->current_image - * so set it temporary to our image */ - struct image_data *saved_current_image = cnt->current_image; - - /* If image is flaged to be saved and not saved yet, process it */ - do { - /* Check if we should save/send this image, breakout if not */ - if ((cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & (IMAGE_SAVE | IMAGE_SAVED)) != IMAGE_SAVE) - break; - - /* Set inte global cotext that we are working with this image */ - cnt->current_image = &cnt->imgs.image_ring[cnt->imgs.image_ring_out]; - - if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot < cnt->conf.frame_limit) { - if (debug_level >= CAMERA_DEBUG) { - char tmp[32]; - const char *t; - - if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_TRIGGER) - t = "Trigger"; - else if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_MOTION) - t = "Motion"; - else if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_PRECAP) - t = "Precap"; - else if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_POSTCAP) - t = "Postcap"; - else - t = "Other"; - - mystrftime(cnt, tmp, sizeof(tmp), "%H%M%S-%q", - &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tm, NULL, 0); - draw_text(cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, 10, 20, cnt->imgs.width, tmp, - cnt->conf.text_double); - draw_text(cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, 10, 30, cnt->imgs.width, t, - cnt->conf.text_double); - } - /* Output the picture to jpegs and ffmpeg */ - event(cnt, EVENT_IMAGE_DETECTED, - cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, NULL, NULL, - &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tm); + /* we are going to send an event, in the events there is still + * some code that use cnt->current_image + * so set it temporary to our image */ + struct image_data *saved_current_image = cnt->current_image; + + /* If image is flaged to be saved and not saved yet, process it */ + do { + /* Check if we should save/send this image, breakout if not */ + if ((cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & (IMAGE_SAVE | IMAGE_SAVED)) != IMAGE_SAVE) + break; + + /* Set inte global cotext that we are working with this image */ + cnt->current_image = &cnt->imgs.image_ring[cnt->imgs.image_ring_out]; + + if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot < cnt->conf.frame_limit) { + if (debug_level >= CAMERA_DEBUG) { + char tmp[32]; + const char *t; + + if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_TRIGGER) + t = "Trigger"; + else if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_MOTION) + t = "Motion"; + else if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_PRECAP) + t = "Precap"; + else if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_POSTCAP) + t = "Postcap"; + else + t = "Other"; + + mystrftime(cnt, tmp, sizeof(tmp), "%H%M%S-%q", + &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tm, NULL, 0); + draw_text(cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, 10, 20, cnt->imgs.width, tmp, + cnt->conf.text_double); + draw_text(cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, 10, 30, cnt->imgs.width, t, + cnt->conf.text_double); + } + /* Output the picture to jpegs and ffmpeg */ + event(cnt, EVENT_IMAGE_DETECTED, + cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, NULL, NULL, + &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tm); #ifdef HAVE_FFMPEG - /* Check if we must add any "filler" frames into movie to keep up fps */ - if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot == 0) { - /* movie_last_shoot is -1 when file is created, - * we don't know how many frames there is in first sec */ - if (cnt->movie_last_shot >= 0) { - if (debug_level >= CAMERA_DEBUG) { - int frames = cnt->movie_fps - (cnt->movie_last_shot + 1); - if (frames > 0) { - char tmp[15]; - motion_log(LOG_DEBUG, 0, "%s: Added %d fillerframes into movie", - __FUNCTION__, frames ); - sprintf(tmp, "Fillerframes %d", frames); - draw_text(cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, 10, 40, - cnt->imgs.width, tmp, cnt->conf.text_double); - } - } - /* Check how many frames it was last sec */ - while( (cnt->movie_last_shot + 1) < cnt->movie_fps ) { - /* Add a filler frame into ffmpeg */ - event(cnt, EVENT_FFMPEG_PUT, - cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, NULL, NULL, - &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tm); - - cnt->movie_last_shot++; - } - } - cnt->movie_last_shot = 0; - } else if ( cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot != (cnt->movie_last_shot + 1)) { - /* We are out of sync! Properbly we got motion - no motion - motion */ - cnt->movie_last_shot = -1; - } - /* Save last shot added to movie - * only when we not are within first sec */ - if (cnt->movie_last_shot >= 0) - cnt->movie_last_shot = cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot; -#endif - } - - /* Mark the image as saved */ - cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags |= IMAGE_SAVED; - - /* Store it as a preview image, only if it have motion */ - if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_MOTION) - { - /* Check for most significant preview-shot when output_pictures=best */ - if (cnt->new_img & NEWIMG_BEST) { - if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].diffs > cnt->imgs.preview_image.diffs) { - image_save_as_preview(cnt, &cnt->imgs.image_ring[cnt->imgs.image_ring_out]); - } - } - /* Check for most significant preview-shot when output_pictures=center */ - if (cnt->new_img & NEWIMG_CENTER) { - if(cnt->imgs.image_ring[cnt->imgs.image_ring_out].cent_dist < cnt->imgs.preview_image.cent_dist) { - image_save_as_preview(cnt, &cnt->imgs.image_ring[cnt->imgs.image_ring_out]); - } - } - } - - /* Increment to image after last sended */ - if (++cnt->imgs.image_ring_out >= cnt->imgs.image_ring_size) - cnt->imgs.image_ring_out = 0; - - if (max_images != IMAGE_BUFFER_FLUSH) { - max_images--; - /* breakout if we have done max_images */ - if (max_images == 0) - break; - } - - /* loop until out and in is same e.g. buffer empty */ - } while (cnt->imgs.image_ring_out != cnt->imgs.image_ring_in); - - /* restore global context values */ - cnt->current_image = saved_current_image; + /* Check if we must add any "filler" frames into movie to keep up fps */ + if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot == 0) { + /* movie_last_shoot is -1 when file is created, + * we don't know how many frames there is in first sec */ + if (cnt->movie_last_shot >= 0) { + if (debug_level >= CAMERA_DEBUG) { + int frames = cnt->movie_fps - (cnt->movie_last_shot + 1); + if (frames > 0) { + char tmp[15]; + motion_log(LOG_DEBUG, 0, "%s: Added %d fillerframes into movie", + __FUNCTION__, frames); + sprintf(tmp, "Fillerframes %d", frames); + draw_text(cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, 10, 40, + cnt->imgs.width, tmp, cnt->conf.text_double); + } + } + /* Check how many frames it was last sec */ + while ((cnt->movie_last_shot + 1) < cnt->movie_fps) { + /* Add a filler frame into ffmpeg */ + event(cnt, EVENT_FFMPEG_PUT, + cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, NULL, NULL, + &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tm); + + cnt->movie_last_shot++; + } + } + cnt->movie_last_shot = 0; + } else if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot != (cnt->movie_last_shot + 1)) { + /* We are out of sync! Properbly we got motion - no motion - motion */ + cnt->movie_last_shot = -1; + } + /* Save last shot added to movie + * only when we not are within first sec */ + if (cnt->movie_last_shot >= 0) + cnt->movie_last_shot = cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot; +#endif + } + + /* Mark the image as saved */ + cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags |= IMAGE_SAVED; + + /* Store it as a preview image, only if it have motion */ + if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_MOTION) { + /* Check for most significant preview-shot when output_pictures=best */ + if (cnt->new_img & NEWIMG_BEST) { + if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].diffs > cnt->imgs.preview_image.diffs) { + image_save_as_preview(cnt, &cnt->imgs.image_ring[cnt->imgs.image_ring_out]); + } + } + /* Check for most significant preview-shot when output_pictures=center */ + if (cnt->new_img & NEWIMG_CENTER) { + if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].cent_dist < cnt->imgs.preview_image.cent_dist) { + image_save_as_preview(cnt, &cnt->imgs.image_ring[cnt->imgs.image_ring_out]); + } + } + } + + /* Increment to image after last sended */ + if (++cnt->imgs.image_ring_out >= cnt->imgs.image_ring_size) + cnt->imgs.image_ring_out = 0; + + if (max_images != IMAGE_BUFFER_FLUSH) { + max_images--; + /* breakout if we have done max_images */ + if (max_images == 0) + break; + } + + /* loop until out and in is same e.g. buffer empty */ + } while (cnt->imgs.image_ring_out != cnt->imgs.image_ring_in); + + /* restore global context values */ + cnt->current_image = saved_current_image; } /** @@ -608,251 +608,259 @@ static void process_image_ring(struct context *cnt, unsigned int max_images) */ static int motion_init(struct context *cnt) { - FILE *picture; - - /* Store thread number in TLS. */ - pthread_setspecific(tls_key_threadnr, (void *)((unsigned long)cnt->threadnr)); - - cnt->currenttime_tm = mymalloc(sizeof(struct tm)); - cnt->eventtime_tm = mymalloc(sizeof(struct tm)); - /* Init frame time */ - cnt->currenttime = time(NULL); - localtime_r(&cnt->currenttime, cnt->currenttime_tm); - - cnt->smartmask_speed = 0; - - /* We initialize cnt->event_nr to 1 and cnt->prev_event to 0 (not really needed) so - * that certain code below does not run until motion has been detected the first time */ - cnt->event_nr = 1; - cnt->prev_event = 0; - cnt->lightswitch_framecounter = 0; - cnt->detecting_motion = 0; - cnt->makemovie = 0; - - motion_log(LOG_DEBUG, 0, "%s: Thread %d started", - __FUNCTION__, (unsigned long)pthread_getspecific(tls_key_threadnr)); - - if (!cnt->conf.filepath) - cnt->conf.filepath = mystrdup("."); - - /* set the device settings */ - cnt->video_dev = vid_start(cnt); - - /* We failed to get an initial image from a camera - * So we need to guess height and width based on the config - * file options. - */ - if (cnt->video_dev < 0) { - motion_log(LOG_ERR, 0, "%s: Could not fetch initial image from camera" - "Motion continues using width and height from config file(s)", - __FUNCTION__); - cnt->imgs.width = cnt->conf.width; - cnt->imgs.height = cnt->conf.height; - cnt->imgs.size = cnt->conf.width * cnt->conf.height * 3 / 2; - cnt->imgs.motionsize = cnt->conf.width * cnt->conf.height; - cnt->imgs.type = VIDEO_PALETTE_YUV420P; - } - - image_ring_resize(cnt, 1); /* Create a initial precapture ring buffer with 1 frame */ - - cnt->imgs.ref = mymalloc(cnt->imgs.size); - cnt->imgs.out = mymalloc(cnt->imgs.size); - memset(cnt->imgs.out, 0, cnt->imgs.size); - cnt->imgs.ref_dyn = mymalloc(cnt->imgs.motionsize * sizeof(cnt->imgs.ref_dyn)); /* contains the moving objects of ref. frame */ - cnt->imgs.image_virgin = mymalloc(cnt->imgs.size); - cnt->imgs.smartmask = mymalloc(cnt->imgs.motionsize); - cnt->imgs.smartmask_final = mymalloc(cnt->imgs.motionsize); - cnt->imgs.smartmask_buffer = mymalloc(cnt->imgs.motionsize * sizeof(cnt->imgs.smartmask_buffer)); - cnt->imgs.labels = mymalloc(cnt->imgs.motionsize * sizeof(cnt->imgs.labels)); - cnt->imgs.labelsize = mymalloc((cnt->imgs.motionsize/2+1) * sizeof(cnt->imgs.labelsize)); - - /* Set output picture type */ - if (!strcmp(cnt->conf.picture_type, "ppm")) - cnt->imgs.picture_type = IMAGE_TYPE_PPM; - else - cnt->imgs.picture_type = IMAGE_TYPE_JPEG; - - /* allocate buffer here for preview buffer */ - cnt->imgs.preview_image.image = mymalloc(cnt->imgs.size); - - /* Allocate a buffer for temp. usage in some places */ - /* Only despeckle & bayer2rgb24() for now for now... */ - cnt->imgs.common_buffer = mymalloc(3 * cnt->imgs.width * cnt->imgs.height); - - /* Now is a good time to init rotation data. Since vid_start has been - * called, we know that we have imgs.width and imgs.height. When capturing - * from a V4L device, these are copied from the corresponding conf values - * in vid_start. When capturing from a netcam, they get set in netcam_start, - * which is called from vid_start. - * - * rotate_init will set cap_width and cap_height in cnt->rotate_data. - */ - rotate_init(cnt); /* rotate_deinit is called in main */ - - /* Capture first image, or we will get an alarm on start */ - if (cnt->video_dev > 0) { - int i; - - for (i = 0; i < 5; i++) { - if (vid_next(cnt, cnt->imgs.image_virgin) == 0) - break; - SLEEP(2, 0); - } - if (i >= 5) { - memset(cnt->imgs.image_virgin, 0x80, cnt->imgs.size); /* initialize to grey */ - draw_text(cnt->imgs.image_virgin, 10, 20, cnt->imgs.width, - "Error capturing first image", cnt->conf.text_double); - motion_log(LOG_ERR, 0, "%s: Error capturing first image", __FUNCTION__); - } - } - - /* create a reference frame */ - alg_update_reference_frame(cnt, RESET_REF_FRAME); + FILE *picture; + + /* Store thread number in TLS. */ + pthread_setspecific(tls_key_threadnr, (void *)((unsigned long)cnt->threadnr)); + + cnt->currenttime_tm = mymalloc(sizeof(struct tm)); + cnt->eventtime_tm = mymalloc(sizeof(struct tm)); + /* Init frame time */ + cnt->currenttime = time(NULL); + localtime_r(&cnt->currenttime, cnt->currenttime_tm); + + cnt->smartmask_speed = 0; + + /* We initialize cnt->event_nr to 1 and cnt->prev_event to 0 (not really needed) so + * that certain code below does not run until motion has been detected the first time */ + cnt->event_nr = 1; + cnt->prev_event = 0; + cnt->lightswitch_framecounter = 0; + cnt->detecting_motion = 0; + cnt->makemovie = 0; + + motion_log(LOG_DEBUG, 0, "%s: Thread %d started", + __FUNCTION__, (unsigned long)pthread_getspecific(tls_key_threadnr)); + + if (!cnt->conf.filepath) + cnt->conf.filepath = mystrdup("."); + + /* set the device settings */ + cnt->video_dev = vid_start(cnt); + + /* We failed to get an initial image from a camera + * So we need to guess height and width based on the config + * file options. + */ + if (cnt->video_dev < 0) { + motion_log(LOG_ERR, 0, "%s: Could not fetch initial image from camera " + "Motion continues using width and height from config file(s)", + __FUNCTION__); + cnt->imgs.width = cnt->conf.width; + cnt->imgs.height = cnt->conf.height; + cnt->imgs.size = cnt->conf.width * cnt->conf.height * 3 / 2; + cnt->imgs.motionsize = cnt->conf.width * cnt->conf.height; + cnt->imgs.type = VIDEO_PALETTE_YUV420P; + } + + image_ring_resize(cnt, 1); /* Create a initial precapture ring buffer with 1 frame */ + + cnt->imgs.ref = mymalloc(cnt->imgs.size); + cnt->imgs.out = mymalloc(cnt->imgs.size); + memset(cnt->imgs.out, 0, cnt->imgs.size); + /* contains the moving objects of ref. frame */ + cnt->imgs.ref_dyn = mymalloc(cnt->imgs.motionsize * sizeof(cnt->imgs.ref_dyn)); + cnt->imgs.image_virgin = mymalloc(cnt->imgs.size); + cnt->imgs.smartmask = mymalloc(cnt->imgs.motionsize); + cnt->imgs.smartmask_final = mymalloc(cnt->imgs.motionsize); + cnt->imgs.smartmask_buffer = mymalloc(cnt->imgs.motionsize * sizeof(cnt->imgs.smartmask_buffer)); + cnt->imgs.labels = mymalloc(cnt->imgs.motionsize * sizeof(cnt->imgs.labels)); + cnt->imgs.labelsize = mymalloc((cnt->imgs.motionsize/2+1) * sizeof(cnt->imgs.labelsize)); + + /* Set output picture type */ + if (!strcmp(cnt->conf.picture_type, "ppm")) + cnt->imgs.picture_type = IMAGE_TYPE_PPM; + else + cnt->imgs.picture_type = IMAGE_TYPE_JPEG; + + /* allocate buffer here for preview buffer */ + cnt->imgs.preview_image.image = mymalloc(cnt->imgs.size); + + /* Allocate a buffer for temp. usage in some places */ + /* Only despeckle & bayer2rgb24() for now for now... */ + cnt->imgs.common_buffer = mymalloc(3 * cnt->imgs.width * cnt->imgs.height); + + /* Now is a good time to init rotation data. Since vid_start has been + * called, we know that we have imgs.width and imgs.height. When capturing + * from a V4L device, these are copied from the corresponding conf values + * in vid_start. When capturing from a netcam, they get set in netcam_start, + * which is called from vid_start. + * + * rotate_init will set cap_width and cap_height in cnt->rotate_data. + */ + rotate_init(cnt); /* rotate_deinit is called in main */ + + /* Capture first image, or we will get an alarm on start */ + if (cnt->video_dev > 0) { + int i; + + for (i = 0; i < 5; i++) { + if (vid_next(cnt, cnt->imgs.image_virgin) == 0) + break; + SLEEP(2, 0); + } + if (i >= 5) { + memset(cnt->imgs.image_virgin, 0x80, cnt->imgs.size); /* initialize to grey */ + draw_text(cnt->imgs.image_virgin, 10, 20, cnt->imgs.width, + "Error capturing first image", cnt->conf.text_double); + motion_log(LOG_ERR, 0, "%s: Error capturing first image", __FUNCTION__); + } + } + + /* create a reference frame */ + alg_update_reference_frame(cnt, RESET_REF_FRAME); #ifndef WITHOUT_V4L #if (!defined(BSD)) - /* open video loopback devices if enabled */ - if (cnt->conf.vidpipe) { - if (debug_level >= CAMERA_DEBUG) - motion_log(-1, 0, "%s: Opening video loopback device for normal pictures", __FUNCTION__); - else - motion_log(LOG_INFO, 0, "%s: Opening video loopback device for normal pictures", __FUNCTION__); - /* vid_startpipe should get the output dimensions */ - cnt->pipe = vid_startpipe(cnt->conf.vidpipe, cnt->imgs.width, cnt->imgs.height, cnt->imgs.type); - if (cnt->pipe < 0) { - motion_log(LOG_ERR, 0, "%s: Failed to open video loopback", __FUNCTION__); - return -1; - } - } - if (cnt->conf.motionvidpipe) { - if (debug_level >= CAMERA_DEBUG) - motion_log(-1, 0, "%s: Opening video loopback device for motion pictures", __FUNCTION__); - else - motion_log(LOG_INFO, 0, "%s: Opening video loopback device for motion pictures", __FUNCTION__); - /* vid_startpipe should get the output dimensions */ - cnt->mpipe = vid_startpipe(cnt->conf.motionvidpipe, cnt->imgs.width, cnt->imgs.height, cnt->imgs.type); - if (cnt->mpipe < 0) { - motion_log(LOG_ERR, 0, "%s: Failed to open video loopback", __FUNCTION__); - return -1; - } - } + /* open video loopback devices if enabled */ + if (cnt->conf.vidpipe) { + if (debug_level >= CAMERA_DEBUG) + motion_log(-1, 0, "%s: Opening video loopback device for normal pictures", __FUNCTION__); + else + motion_log(LOG_INFO, 0, "%s: Opening video loopback device for normal pictures", __FUNCTION__); + /* vid_startpipe should get the output dimensions */ + cnt->pipe = vid_startpipe(cnt->conf.vidpipe, cnt->imgs.width, cnt->imgs.height, cnt->imgs.type); + if (cnt->pipe < 0) { + motion_log(LOG_ERR, 0, "%s: Failed to open video loopback", __FUNCTION__); + return -1; + } + } + + if (cnt->conf.motionvidpipe) { + if (debug_level >= CAMERA_DEBUG) + motion_log(-1, 0, "%s: Opening video loopback device for motion pictures", __FUNCTION__); + else + motion_log(LOG_INFO, 0, "%s: Opening video loopback device for motion pictures", __FUNCTION__); + + /* vid_startpipe should get the output dimensions */ + cnt->mpipe = vid_startpipe(cnt->conf.motionvidpipe, cnt->imgs.width, cnt->imgs.height, cnt->imgs.type); + + if (cnt->mpipe < 0) { + motion_log(LOG_ERR, 0, "%s: Failed to open video loopback", __FUNCTION__); + return -1; + } + } #endif /* BSD */ #endif /*WITHOUT_V4L*/ #if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) - if (cnt->conf.database_type) { + if (cnt->conf.database_type) { #ifdef HAVE_MYSQL - if ((!strcmp(cnt->conf.database_type, "mysql")) && (cnt->conf.database_dbname)) { - cnt->database = (MYSQL *) mymalloc(sizeof(MYSQL)); - mysql_init(cnt->database); - if (!mysql_real_connect(cnt->database, cnt->conf.database_host, cnt->conf.database_user, - cnt->conf.database_password, cnt->conf.database_dbname, 0, NULL, 0)) { - motion_log(LOG_ERR, 0, "%s: Cannot connect to MySQL database %s on host %s with user %s", - __FUNCTION__, cnt->conf.database_dbname, cnt->conf.database_host, - cnt->conf.database_user); - motion_log(LOG_ERR, 0, "%s: MySQL error was %s", __FUNCTION__, mysql_error(cnt->database)); - return -2; - } - #if (defined(MYSQL_VERSION_ID)) && (MYSQL_VERSION_ID > 50012) - my_bool my_true = TRUE; - mysql_options(cnt->database, MYSQL_OPT_RECONNECT, &my_true); - #endif - } + if ((!strcmp(cnt->conf.database_type, "mysql")) && (cnt->conf.database_dbname)) { + cnt->database = (MYSQL *) mymalloc(sizeof(MYSQL)); + mysql_init(cnt->database); + + if (!mysql_real_connect(cnt->database, cnt->conf.database_host, cnt->conf.database_user, + cnt->conf.database_password, cnt->conf.database_dbname, 0, NULL, 0)) { + motion_log(LOG_ERR, 0, "%s: Cannot connect to MySQL database %s on host %s with user %s", + __FUNCTION__, cnt->conf.database_dbname, cnt->conf.database_host, + cnt->conf.database_user); + motion_log(LOG_ERR, 0, "%s: MySQL error was %s", __FUNCTION__, mysql_error(cnt->database)); + return -2; + } + #if (defined(MYSQL_VERSION_ID)) && (MYSQL_VERSION_ID > 50012) + my_bool my_true = TRUE; + mysql_options(cnt->database, MYSQL_OPT_RECONNECT, &my_true); + #endif + } #endif /* HAVE_MYSQL */ #ifdef HAVE_PGSQL - if ((!strcmp(cnt->conf.database_type, "postgresql")) && (cnt->conf.database_dbname)) { - char connstring[255]; - - /* create the connection string. - Quote the values so we can have null values (blank)*/ - snprintf(connstring, 255, - "dbname='%s' host='%s' user='%s' password='%s' port='%d'", - cnt->conf.database_dbname, /* dbname */ - (cnt->conf.database_host ? cnt->conf.database_host : ""), /* host (may be blank) */ - (cnt->conf.database_user ? cnt->conf.database_user : ""), /* user (may be blank) */ - (cnt->conf.database_password ? cnt->conf.database_password : ""), /* password (may be blank) */ - cnt->conf.database_port - ); - - cnt->database_pg = PQconnectdb(connstring); - if (PQstatus(cnt->database_pg) == CONNECTION_BAD) { - motion_log(LOG_ERR, 0, "%s: Connection to PostgreSQL database '%s' failed: %s", - __FUNCTION__, cnt->conf.database_dbname, PQerrorMessage(cnt->database_pg)); - return -2; - } - } + if ((!strcmp(cnt->conf.database_type, "postgresql")) && (cnt->conf.database_dbname)) { + char connstring[255]; + + /* create the connection string. + Quote the values so we can have null values (blank)*/ + snprintf(connstring, 255, + "dbname='%s' host='%s' user='%s' password='%s' port='%d'", + cnt->conf.database_dbname, /* dbname */ + (cnt->conf.database_host ? cnt->conf.database_host : ""), /* host (may be blank) */ + (cnt->conf.database_user ? cnt->conf.database_user : ""), /* user (may be blank) */ + (cnt->conf.database_password ? cnt->conf.database_password : ""), /* password (may be blank) */ + cnt->conf.database_port + ); + + cnt->database_pg = PQconnectdb(connstring); + if (PQstatus(cnt->database_pg) == CONNECTION_BAD) { + motion_log(LOG_ERR, 0, "%s: Connection to PostgreSQL database '%s' failed: %s", + __FUNCTION__, cnt->conf.database_dbname, PQerrorMessage(cnt->database_pg)); + return -2; + } + } #endif /* HAVE_PGSQL */ - + - /* Set the sql mask file according to the SQL config options*/ + /* Set the sql mask file according to the SQL config options*/ - cnt->sql_mask = cnt->conf.sql_log_image * (FTYPE_IMAGE + FTYPE_IMAGE_MOTION) + - cnt->conf.sql_log_snapshot * FTYPE_IMAGE_SNAPSHOT + - cnt->conf.sql_log_movie * (FTYPE_MPEG + FTYPE_MPEG_MOTION) + - cnt->conf.sql_log_timelapse * FTYPE_MPEG_TIMELAPSE; - } + cnt->sql_mask = cnt->conf.sql_log_image * (FTYPE_IMAGE + FTYPE_IMAGE_MOTION) + + cnt->conf.sql_log_snapshot * FTYPE_IMAGE_SNAPSHOT + + cnt->conf.sql_log_movie * (FTYPE_MPEG + FTYPE_MPEG_MOTION) + + cnt->conf.sql_log_timelapse * FTYPE_MPEG_TIMELAPSE; + } #endif /* defined(HAVE_MYSQL) || defined(HAVE_PGSQL) */ - /* Load the mask file if any */ - if (cnt->conf.mask_file) { - if ((picture = fopen(cnt->conf.mask_file, "r"))) { - /* NOTE: The mask is expected to have the output dimensions. I.e., the mask - * applies to the already rotated image, not the capture image. Thus, use - * width and height from imgs. - */ - cnt->imgs.mask = get_pgm(picture, cnt->imgs.width, cnt->imgs.height); - fclose(picture); - } else { - motion_log(LOG_ERR, 1, "%s: Error opening mask file %s", - __FUNCTION__, cnt->conf.mask_file); - /* Try to write an empty mask file to make it easier - for the user to edit it */ - put_fixed_mask(cnt, cnt->conf.mask_file); - } - if (!cnt->imgs.mask) { - motion_log(LOG_ERR, 0, "%s: Failed to read mask image. Mask feature disabled.", - __FUNCTION__); - } else { - if (debug_level >= CAMERA_DEBUG) - motion_log(-1, 0, "%s: Maskfile \"%s\" loaded.", - __FUNCTION__, cnt->conf.mask_file); - else - motion_log(LOG_INFO, 0, "%s: Maskfile \"%s\" loaded.", - __FUNCTION__, cnt->conf.mask_file); - } - } else - cnt->imgs.mask = NULL; - - /* Always initialize smart_mask - someone could turn it on later... */ - memset(cnt->imgs.smartmask, 0, cnt->imgs.motionsize); - memset(cnt->imgs.smartmask_final, 255, cnt->imgs.motionsize); - memset(cnt->imgs.smartmask_buffer, 0, cnt->imgs.motionsize*sizeof(cnt->imgs.smartmask_buffer)); - - /* Set noise level */ - cnt->noise = cnt->conf.noise; - - /* Set threshold value */ - cnt->threshold = cnt->conf.max_changes; - - /* Initialize stream server if stream port is specified to not 0 */ - if (cnt->conf.stream_port) { - if ( stream_init(cnt) == -1 ) { - motion_log(LOG_ERR, 1, "%s: Problem enabling motion-stream server in port %d", - __FUNCTION__, cnt->conf.stream_port); - cnt->finish = 1; - }else motion_log(LOG_DEBUG, 0, "%s: Started motion-stream server in port %d", - __FUNCTION__, cnt->conf.stream_port); - } - - /* Prevent first few frames from triggering motion... */ - cnt->moved = 8; - /* 2 sec startup delay so FPS is calculated correct */ - cnt->startup_frames = cnt->conf.frame_limit * 2; - - return 0; + /* Load the mask file if any */ + if (cnt->conf.mask_file) { + if ((picture = fopen(cnt->conf.mask_file, "r"))) { + /* NOTE: The mask is expected to have the output dimensions. I.e., the mask + * applies to the already rotated image, not the capture image. Thus, use + * width and height from imgs. + */ + cnt->imgs.mask = get_pgm(picture, cnt->imgs.width, cnt->imgs.height); + fclose(picture); + } else { + motion_log(LOG_ERR, 1, "%s: Error opening mask file %s", + __FUNCTION__, cnt->conf.mask_file); + /* Try to write an empty mask file to make it easier + for the user to edit it */ + put_fixed_mask(cnt, cnt->conf.mask_file); + } + if (!cnt->imgs.mask) { + motion_log(LOG_ERR, 0, "%s: Failed to read mask image. Mask feature disabled.", + __FUNCTION__); + } else { + if (debug_level >= CAMERA_DEBUG) + motion_log(-1, 0, "%s: Maskfile \"%s\" loaded.", + __FUNCTION__, cnt->conf.mask_file); + else + motion_log(LOG_INFO, 0, "%s: Maskfile \"%s\" loaded.", + __FUNCTION__, cnt->conf.mask_file); + } + } else { + cnt->imgs.mask = NULL; + } + + /* Always initialize smart_mask - someone could turn it on later... */ + memset(cnt->imgs.smartmask, 0, cnt->imgs.motionsize); + memset(cnt->imgs.smartmask_final, 255, cnt->imgs.motionsize); + memset(cnt->imgs.smartmask_buffer, 0, cnt->imgs.motionsize*sizeof(cnt->imgs.smartmask_buffer)); + + /* Set noise level */ + cnt->noise = cnt->conf.noise; + + /* Set threshold value */ + cnt->threshold = cnt->conf.max_changes; + + /* Initialize stream server if stream port is specified to not 0 */ + if (cnt->conf.stream_port) { + if (stream_init(cnt) == -1) { + motion_log(LOG_ERR, 1, "%s: Problem enabling motion-stream server in port %d", + __FUNCTION__, cnt->conf.stream_port); + cnt->finish = 1; + } else { + motion_log(LOG_DEBUG, 0, "%s: Started motion-stream server in port %d", + __FUNCTION__, cnt->conf.stream_port); + } + } + + /* Prevent first few frames from triggering motion... */ + cnt->moved = 8; + /* 2 sec startup delay so FPS is calculated correct */ + cnt->startup_frames = cnt->conf.frame_limit * 2; + + return 0; } /** @@ -869,84 +877,94 @@ static int motion_init(struct context *cnt) */ static void motion_cleanup(struct context *cnt) { - /* Stop stream */ - event(cnt, EVENT_STOP, NULL, NULL, NULL, NULL); - - if (cnt->video_dev >= 0){ - motion_log(LOG_DEBUG, 0, "%s: Calling vid_close() from motion_cleanup", __FUNCTION__); - vid_close(cnt); - } - - if (cnt->imgs.out) { - free(cnt->imgs.out); - cnt->imgs.out = NULL; - } - if (cnt->imgs.ref) { - free(cnt->imgs.ref); - cnt->imgs.ref = NULL; - } - if (cnt->imgs.ref_dyn) { - free(cnt->imgs.ref_dyn); - cnt->imgs.ref_dyn = NULL; - } - if (cnt->imgs.image_virgin) { - free(cnt->imgs.image_virgin); - cnt->imgs.image_virgin = NULL; - } - if (cnt->imgs.labels) { - free(cnt->imgs.labels); - cnt->imgs.labels = NULL; - } - if (cnt->imgs.labelsize) { - free(cnt->imgs.labelsize); - cnt->imgs.labelsize = NULL; - } - if (cnt->imgs.smartmask) { - free(cnt->imgs.smartmask); - cnt->imgs.smartmask = NULL; - } - if (cnt->imgs.smartmask_final) { - free(cnt->imgs.smartmask_final); - cnt->imgs.smartmask_final = NULL; - } - if (cnt->imgs.smartmask_buffer) { - free(cnt->imgs.smartmask_buffer); - cnt->imgs.smartmask_buffer = NULL; - } - if (cnt->imgs.common_buffer) { - free(cnt->imgs.common_buffer); - cnt->imgs.common_buffer = NULL; - } - if (cnt->imgs.preview_image.image) { - free(cnt->imgs.preview_image.image); - cnt->imgs.preview_image.image = NULL; - } - - image_ring_destroy(cnt); /* Cleanup the precapture ring buffer */ - - rotate_deinit(cnt); /* cleanup image rotation data */ - - if (cnt->pipe != -1) { - close(cnt->pipe); - cnt->pipe = -1; - } - - if (cnt->mpipe != -1) { - close(cnt->mpipe); - cnt->mpipe = -1; - } - - /* Cleanup the current time structure */ - if (cnt->currenttime_tm) { - free(cnt->currenttime_tm); - cnt->currenttime_tm = NULL; - } - - /* Cleanup the event time structure */ - if (cnt->eventtime_tm) { - free(cnt->eventtime_tm); - cnt->eventtime_tm = NULL; - } + /* Stop stream */ + event(cnt, EVENT_STOP, NULL, NULL, NULL, NULL); + + if (cnt->video_dev >= 0) { + motion_log(LOG_DEBUG, 0, "%s: Calling vid_close() from motion_cleanup", __FUNCTION__); + vid_close(cnt); + } + + if (cnt->imgs.out) { + free(cnt->imgs.out); + cnt->imgs.out = NULL; + } + + if (cnt->imgs.ref) { + free(cnt->imgs.ref); + cnt->imgs.ref = NULL; + } + + if (cnt->imgs.ref_dyn) { + free(cnt->imgs.ref_dyn); + cnt->imgs.ref_dyn = NULL; + } + + if (cnt->imgs.image_virgin) { + free(cnt->imgs.image_virgin); + cnt->imgs.image_virgin = NULL; + } + + if (cnt->imgs.labels) { + free(cnt->imgs.labels); + cnt->imgs.labels = NULL; + } + + if (cnt->imgs.labelsize) { + free(cnt->imgs.labelsize); + cnt->imgs.labelsize = NULL; + } + + if (cnt->imgs.smartmask) { + free(cnt->imgs.smartmask); + cnt->imgs.smartmask = NULL; + } + + if (cnt->imgs.smartmask_final) { + free(cnt->imgs.smartmask_final); + cnt->imgs.smartmask_final = NULL; + } + + if (cnt->imgs.smartmask_buffer) { + free(cnt->imgs.smartmask_buffer); + cnt->imgs.smartmask_buffer = NULL; + } + + if (cnt->imgs.common_buffer) { + free(cnt->imgs.common_buffer); + cnt->imgs.common_buffer = NULL; + } + + if (cnt->imgs.preview_image.image) { + free(cnt->imgs.preview_image.image); + cnt->imgs.preview_image.image = NULL; + } + + image_ring_destroy(cnt); /* Cleanup the precapture ring buffer */ + + rotate_deinit(cnt); /* cleanup image rotation data */ + + if (cnt->pipe != -1) { + close(cnt->pipe); + cnt->pipe = -1; + } + + if (cnt->mpipe != -1) { + close(cnt->mpipe); + cnt->mpipe = -1; + } + + /* Cleanup the current time structure */ + if (cnt->currenttime_tm) { + free(cnt->currenttime_tm); + cnt->currenttime_tm = NULL; + } + + /* Cleanup the event time structure */ + if (cnt->eventtime_tm) { + free(cnt->eventtime_tm); + cnt->eventtime_tm = NULL; + } } /** @@ -957,1005 +975,1044 @@ static void motion_cleanup(struct context *cnt) */ static void *motion_loop(void *arg) { - struct context *cnt = arg; - int i, j, z = 0; - time_t lastframetime = 0; - int frame_buffer_size; - unsigned short int ref_frame_limit = 0; - int area_once = 0; - int area_minx[9], area_miny[9], area_maxx[9], area_maxy[9]; - int smartmask_ratio = 0; - int smartmask_count = 20; - int smartmask_lastrate = 0; - int olddiffs = 0; - int previous_diffs = 0, previous_location_x = 0, previous_location_y = 0; - unsigned short int text_size_factor; - unsigned short int passflag = 0; - long int *rolling_average_data = NULL; - long int rolling_average_limit, required_frame_time, frame_delay, delay_time_nsec; - int rolling_frame = 0; - struct timeval tv1, tv2; - unsigned long int rolling_average, elapsedtime; - unsigned long long int timenow = 0, timebefore = 0; - int vid_return_code = 0; /* Return code used when calling vid_next */ - int minimum_frame_time_downcounter = cnt->conf.minimum_frame_time; /* time in seconds to skip between capturing images */ - unsigned short int get_image = 1; /* Flag used to signal that we capture new image when we run the loop */ - - /* Next two variables are used for snapshot and timelapse feature - * time_last_frame is set to 1 so that first coming timelapse or second = 0 - * is acted upon. - */ - unsigned long int time_last_frame = 1, time_current_frame; - - cnt->running = 1; - - if (motion_init(cnt) < 0) { - goto err; - } - - /* Initialize the double sized characters if needed. */ - if(cnt->conf.text_double) - text_size_factor = 2; - else - text_size_factor = 1; - - /* Initialize area detection */ - area_minx[0] = area_minx[3] = area_minx[6] = area_miny[0] = area_miny[1] = area_miny[2] = 0; - area_minx[1] = area_minx[4] = area_minx[7] = area_maxx[0] = area_maxx[3] = area_maxx[6] = cnt->imgs.width / 3; - area_minx[2] = area_minx[5] = area_minx[8] = area_maxx[1] = area_maxx[4] = area_maxx[7] = cnt->imgs.width / 3 * 2; - area_miny[3] = area_miny[4] = area_miny[5] = area_maxy[0] = area_maxy[1] = area_maxy[2] = cnt->imgs.height / 3; - area_miny[6] = area_miny[7] = area_miny[8] = area_maxy[3] = area_maxy[4] = area_maxy[5] = cnt->imgs.height / 3 * 2; - area_maxx[2] = area_maxx[5] = area_maxx[8] = cnt->imgs.width; - area_maxy[6] = area_maxy[7] = area_maxy[8] = cnt->imgs.height; - - /* Work out expected frame rate based on config setting */ - if (cnt->conf.frame_limit < 2) cnt->conf.frame_limit = 2; - - required_frame_time = 1000000L / cnt->conf.frame_limit; - - frame_delay = required_frame_time; - - /* - * Reserve enough space for a 10 second timing history buffer. Note that, - * if there is any problem on the allocation, mymalloc does not return. - */ - rolling_average_limit = 10 * cnt->conf.frame_limit; - rolling_average_data = mymalloc(sizeof(rolling_average_data) * rolling_average_limit); - - /* Preset history buffer with expected frame rate */ - for (j = 0; j < rolling_average_limit; j++) - rolling_average_data[j] = required_frame_time; - - - if (cnt->track.type) - cnt->moved = track_center(cnt, cnt->video_dev, 0, 0, 0); - - /* MAIN MOTION LOOP BEGINS HERE */ - /* Should go on forever... unless you bought vaporware :) */ - - while (!cnt->finish || cnt->makemovie) { - - /***** MOTION LOOP - PREPARE FOR NEW FRAME SECTION *****/ - cnt->watchdog = WATCHDOG_TMO; - - /* Get current time and preserver last time for frame interval calc. */ - timebefore = timenow; - gettimeofday(&tv1, NULL); - timenow = tv1.tv_usec + 1000000L * tv1.tv_sec; - - /* since we don't have sanity checks done when options are set, - * this sanity check must go in the main loop :(, before pre_captures - * are attempted. */ - if (cnt->conf.minimum_motion_frames < 1) - cnt->conf.minimum_motion_frames = 1; - if (cnt->conf.pre_capture < 0) - cnt->conf.pre_capture = 0; - - /* Check if our buffer is still the right size - * If pre_capture or minimum_motion_frames has been changed - * via the http remote control we need to re-size the ring buffer - */ - frame_buffer_size = cnt->conf.pre_capture + cnt->conf.minimum_motion_frames; - if (cnt->imgs.image_ring_size != frame_buffer_size) { - image_ring_resize(cnt, frame_buffer_size); - } - - /* Get time for current frame */ - cnt->currenttime = time(NULL); - - /* localtime returns static data and is not threadsafe - * so we use localtime_r which is reentrant and threadsafe - */ - localtime_r(&cnt->currenttime, cnt->currenttime_tm); - - /* If we have started on a new second we reset the shots variable - * lastrate is updated to be the number of the last frame. last rate - * is used as the ffmpeg framerate when motion is detected. - */ - if (lastframetime != cnt->currenttime) { - cnt->lastrate = cnt->shots + 1; - cnt->shots = -1; - lastframetime = cnt->currenttime; - if (cnt->conf.minimum_frame_time) { - minimum_frame_time_downcounter--; - if (minimum_frame_time_downcounter == 0) - get_image = 1; - } - else - get_image = 1; - } - - - /* Increase the shots variable for each frame captured within this second */ - cnt->shots++; - - if (cnt->startup_frames > 0) - cnt->startup_frames--; - - if (get_image){ - if (cnt->conf.minimum_frame_time) { - minimum_frame_time_downcounter = cnt->conf.minimum_frame_time; - get_image = 0; - } - - /* ring_buffer_in is pointing to current pos, update before put in a new image */ - if (++cnt->imgs.image_ring_in >= cnt->imgs.image_ring_size) - cnt->imgs.image_ring_in = 0; - - /* Check if we have filled the ring buffer, throw away last image */ - if (cnt->imgs.image_ring_in == cnt->imgs.image_ring_out) { - if (++cnt->imgs.image_ring_out >= cnt->imgs.image_ring_size) - cnt->imgs.image_ring_out = 0; - } - - /* cnt->current_image points to position in ring where to store image, diffs etc. */ - cnt->current_image = &cnt->imgs.image_ring[cnt->imgs.image_ring_in]; - - /* Init/clear current_image */ - { - /* Store time with pre_captured image */ - cnt->current_image->timestamp = cnt->currenttime; - localtime_r(&cnt->current_image->timestamp, &cnt->current_image->timestamp_tm); - - /* Store shot number with pre_captured image */ - cnt->current_image->shot = cnt->shots; - - /* set diffs to 0 now, will be written after we calculated diffs in new image */ - cnt->current_image->diffs = 0; - - /* Set flags to 0 */ - cnt->current_image->flags = 0; - cnt->current_image->cent_dist = 0; - - /* Clear location data */ - memset(&cnt->current_image->location, 0, sizeof(cnt->current_image->location)); - cnt->current_image->total_labels = 0; - } - - /***** MOTION LOOP - RETRY INITIALIZING SECTION *****/ - /* If a camera is not available we keep on retrying every 10 seconds - * until it shows up. - */ - if (cnt->video_dev < 0 && - cnt->currenttime % 10 == 0 && cnt->shots == 0) { - motion_log(LOG_ERR, 0, - "%s: Retrying until successful connection with camera", __FUNCTION__); - cnt->video_dev = vid_start(cnt); - - /* if the netcam has different dimensions than in the config file - * we need to restart Motion to re-allocate all the buffers - */ - if (cnt->imgs.width != cnt->conf.width || cnt->imgs.height != cnt->conf.height) { - motion_log(LOG_ERR, 0, "%s: Camera has finally become available\n" - "Camera image has different width and height" - "from what is in the config file. You should fix that\n" - "Restarting Motion thread to reinitialize all " - "image buffers to new picture dimensions", __FUNCTION__); - cnt->conf.width = cnt->imgs.width; - cnt->conf.height = cnt->imgs.height; - /* Break out of main loop terminating thread - * watchdog will start us again */ - break; - } - } - - - /***** MOTION LOOP - IMAGE CAPTURE SECTION *****/ - - /* Fetch next frame from camera - * If vid_next returns 0 all is well and we got a new picture - * Any non zero value is an error. - * 0 = OK, valid picture - * <0 = fatal error - leave the thread by breaking out of the main loop - * >0 = non fatal error - copy last image or show grey image with message - */ - if (cnt->video_dev >= 0) - vid_return_code = vid_next(cnt, cnt->current_image->image); - else - vid_return_code = 1; /* Non fatal error */ - - // VALID PICTURE - if (vid_return_code == 0) { - cnt->lost_connection = 0; - cnt->connectionlosttime = 0; - - /* If all is well reset missing_frame_counter */ - if (cnt->missing_frame_counter >= MISSING_FRAMES_TIMEOUT * cnt->conf.frame_limit) { - /* If we previously logged starting a grey image, now log video re-start */ - motion_log(LOG_ERR, 0, "%s: Video signal re-acquired", __FUNCTION__); - // event for re-acquired video signal can be called here - } - cnt->missing_frame_counter = 0; + struct context *cnt = arg; + int i, j, z = 0; + time_t lastframetime = 0; + int frame_buffer_size; + unsigned short int ref_frame_limit = 0; + int area_once = 0; + int area_minx[9], area_miny[9], area_maxx[9], area_maxy[9]; + int smartmask_ratio = 0; + int smartmask_count = 20; + int smartmask_lastrate = 0; + int olddiffs = 0; + int previous_diffs = 0, previous_location_x = 0, previous_location_y = 0; + unsigned short int text_size_factor; + unsigned short int passflag = 0; + long int *rolling_average_data = NULL; + long int rolling_average_limit, required_frame_time, frame_delay, delay_time_nsec; + int rolling_frame = 0; + struct timeval tv1, tv2; + unsigned long int rolling_average, elapsedtime; + unsigned long long int timenow = 0, timebefore = 0; + int vid_return_code = 0; /* Return code used when calling vid_next */ + int minimum_frame_time_downcounter = cnt->conf.minimum_frame_time; /* time in seconds to skip between capturing images */ + unsigned short int get_image = 1; /* Flag used to signal that we capture new image when we run the loop */ + + /* Next two variables are used for snapshot and timelapse feature + * time_last_frame is set to 1 so that first coming timelapse or second = 0 + * is acted upon. + */ + unsigned long int time_last_frame = 1, time_current_frame; + + cnt->running = 1; + + if (motion_init(cnt) < 0) + goto err; + + + /* Initialize the double sized characters if needed. */ + if (cnt->conf.text_double) + text_size_factor = 2; + else + text_size_factor = 1; + + /* Initialize area detection */ + area_minx[0] = area_minx[3] = area_minx[6] = area_miny[0] = area_miny[1] = area_miny[2] = 0; + area_minx[1] = area_minx[4] = area_minx[7] = area_maxx[0] = area_maxx[3] = area_maxx[6] = cnt->imgs.width / 3; + area_minx[2] = area_minx[5] = area_minx[8] = area_maxx[1] = area_maxx[4] = area_maxx[7] = cnt->imgs.width / 3 * 2; + area_miny[3] = area_miny[4] = area_miny[5] = area_maxy[0] = area_maxy[1] = area_maxy[2] = cnt->imgs.height / 3; + area_miny[6] = area_miny[7] = area_miny[8] = area_maxy[3] = area_maxy[4] = area_maxy[5] = cnt->imgs.height / 3 * 2; + area_maxx[2] = area_maxx[5] = area_maxx[8] = cnt->imgs.width; + area_maxy[6] = area_maxy[7] = area_maxy[8] = cnt->imgs.height; + + /* Work out expected frame rate based on config setting */ + if (cnt->conf.frame_limit < 2) cnt->conf.frame_limit = 2; + + required_frame_time = 1000000L / cnt->conf.frame_limit; + + frame_delay = required_frame_time; + + /* + * Reserve enough space for a 10 second timing history buffer. Note that, + * if there is any problem on the allocation, mymalloc does not return. + */ + rolling_average_limit = 10 * cnt->conf.frame_limit; + rolling_average_data = mymalloc(sizeof(rolling_average_data) * rolling_average_limit); + + /* Preset history buffer with expected frame rate */ + for (j = 0; j < rolling_average_limit; j++) + rolling_average_data[j] = required_frame_time; + + + if (cnt->track.type) + cnt->moved = track_center(cnt, cnt->video_dev, 0, 0, 0); + + /* MAIN MOTION LOOP BEGINS HERE */ + /* Should go on forever... unless you bought vaporware :) */ + + while (!cnt->finish || cnt->makemovie) { + + /***** MOTION LOOP - PREPARE FOR NEW FRAME SECTION *****/ + cnt->watchdog = WATCHDOG_TMO; + + /* Get current time and preserver last time for frame interval calc. */ + timebefore = timenow; + gettimeofday(&tv1, NULL); + timenow = tv1.tv_usec + 1000000L * tv1.tv_sec; + + /* since we don't have sanity checks done when options are set, + * this sanity check must go in the main loop :(, before pre_captures + * are attempted. */ + if (cnt->conf.minimum_motion_frames < 1) + cnt->conf.minimum_motion_frames = 1; + if (cnt->conf.pre_capture < 0) + cnt->conf.pre_capture = 0; + + /* Check if our buffer is still the right size + * If pre_capture or minimum_motion_frames has been changed + * via the http remote control we need to re-size the ring buffer + */ + frame_buffer_size = cnt->conf.pre_capture + cnt->conf.minimum_motion_frames; + + if (cnt->imgs.image_ring_size != frame_buffer_size) + image_ring_resize(cnt, frame_buffer_size); + + + /* Get time for current frame */ + cnt->currenttime = time(NULL); + + /* localtime returns static data and is not threadsafe + * so we use localtime_r which is reentrant and threadsafe + */ + localtime_r(&cnt->currenttime, cnt->currenttime_tm); + + /* If we have started on a new second we reset the shots variable + * lastrate is updated to be the number of the last frame. last rate + * is used as the ffmpeg framerate when motion is detected. + */ + if (lastframetime != cnt->currenttime) { + cnt->lastrate = cnt->shots + 1; + cnt->shots = -1; + lastframetime = cnt->currenttime; + + if (cnt->conf.minimum_frame_time) { + minimum_frame_time_downcounter--; + if (minimum_frame_time_downcounter == 0) + get_image = 1; + } else { + get_image = 1; + } + } + + + /* Increase the shots variable for each frame captured within this second */ + cnt->shots++; + + if (cnt->startup_frames > 0) + cnt->startup_frames--; + + if (get_image) { + if (cnt->conf.minimum_frame_time) { + minimum_frame_time_downcounter = cnt->conf.minimum_frame_time; + get_image = 0; + } + + /* ring_buffer_in is pointing to current pos, update before put in a new image */ + if (++cnt->imgs.image_ring_in >= cnt->imgs.image_ring_size) + cnt->imgs.image_ring_in = 0; + + /* Check if we have filled the ring buffer, throw away last image */ + if (cnt->imgs.image_ring_in == cnt->imgs.image_ring_out) { + if (++cnt->imgs.image_ring_out >= cnt->imgs.image_ring_size) + cnt->imgs.image_ring_out = 0; + } + + /* cnt->current_image points to position in ring where to store image, diffs etc. */ + cnt->current_image = &cnt->imgs.image_ring[cnt->imgs.image_ring_in]; + + /* Init/clear current_image */ + { + /* Store time with pre_captured image */ + cnt->current_image->timestamp = cnt->currenttime; + localtime_r(&cnt->current_image->timestamp, &cnt->current_image->timestamp_tm); + + /* Store shot number with pre_captured image */ + cnt->current_image->shot = cnt->shots; + + /* set diffs to 0 now, will be written after we calculated diffs in new image */ + cnt->current_image->diffs = 0; + + /* Set flags to 0 */ + cnt->current_image->flags = 0; + cnt->current_image->cent_dist = 0; + + /* Clear location data */ + memset(&cnt->current_image->location, 0, sizeof(cnt->current_image->location)); + cnt->current_image->total_labels = 0; + } + + /***** MOTION LOOP - RETRY INITIALIZING SECTION *****/ + /* If a camera is not available we keep on retrying every 10 seconds + * until it shows up. + */ + if (cnt->video_dev < 0 && + cnt->currenttime % 10 == 0 && cnt->shots == 0) { + motion_log(LOG_ERR, 0, + "%s: Retrying until successful connection with camera", __FUNCTION__); + cnt->video_dev = vid_start(cnt); + + /* if the netcam has different dimensions than in the config file + * we need to restart Motion to re-allocate all the buffers + */ + if (cnt->imgs.width != cnt->conf.width || cnt->imgs.height != cnt->conf.height) { + motion_log(LOG_ERR, 0, "%s: Camera has finally become available\n" + "Camera image has different width and height" + "from what is in the config file. You should fix that\n" + "Restarting Motion thread to reinitialize all " + "image buffers to new picture dimensions", __FUNCTION__); + cnt->conf.width = cnt->imgs.width; + cnt->conf.height = cnt->imgs.height; + /* Break out of main loop terminating thread + * watchdog will start us again */ + break; + } + } + + + /***** MOTION LOOP - IMAGE CAPTURE SECTION *****/ + + /* Fetch next frame from camera + * If vid_next returns 0 all is well and we got a new picture + * Any non zero value is an error. + * 0 = OK, valid picture + * <0 = fatal error - leave the thread by breaking out of the main loop + * >0 = non fatal error - copy last image or show grey image with message + */ + if (cnt->video_dev >= 0) + vid_return_code = vid_next(cnt, cnt->current_image->image); + else + vid_return_code = 1; /* Non fatal error */ + + // VALID PICTURE + if (vid_return_code == 0) { + cnt->lost_connection = 0; + cnt->connectionlosttime = 0; + + /* If all is well reset missing_frame_counter */ + if (cnt->missing_frame_counter >= MISSING_FRAMES_TIMEOUT * cnt->conf.frame_limit) { + /* If we previously logged starting a grey image, now log video re-start */ + motion_log(LOG_ERR, 0, "%s: Video signal re-acquired", __FUNCTION__); + // event for re-acquired video signal can be called here + } + cnt->missing_frame_counter = 0; #ifdef HAVE_FFMPEG - /* Deinterlace the image with ffmpeg, before the image is modified. */ - if(cnt->conf.ffmpeg_deinterlace) { - ffmpeg_deinterlace(cnt->current_image->image, cnt->imgs.width, cnt->imgs.height); - } + /* Deinterlace the image with ffmpeg, before the image is modified. */ + if (cnt->conf.ffmpeg_deinterlace) + ffmpeg_deinterlace(cnt->current_image->image, cnt->imgs.width, cnt->imgs.height); + #endif - /* save the newly captured still virgin image to a buffer - * which we will not alter with text and location graphics - */ - memcpy(cnt->imgs.image_virgin, cnt->current_image->image, cnt->imgs.size); - - /* If the camera is a netcam we let the camera decide the pace. - * Otherwise we will keep on adding duplicate frames. - * By resetting the timer the framerate becomes maximum the rate - * of the Netcam. - */ - if (cnt->conf.netcam_url) { - gettimeofday(&tv1, NULL); - timenow = tv1.tv_usec + 1000000L * tv1.tv_sec; - } - // FATAL ERROR - leave the thread by breaking out of the main loop - } else if (vid_return_code < 0) { - /* Fatal error - Close video device */ - motion_log(LOG_ERR, 0, "%s: Video device fatal error - Closing video device", - __FUNCTION__); - vid_close(cnt); - /* Use virgin image, if we are not able to open it again next loop - * a gray image with message is applied - * flag lost_connection - */ - memcpy(cnt->current_image->image, cnt->imgs.image_virgin, cnt->imgs.size); - cnt->lost_connection = 1; - /* NO FATAL ERROR - - * copy last image or show grey image with message - * flag on lost_connection if : - * vid_return_code == NETCAM_RESTART_ERROR - * cnt->video_dev < 0 - * cnt->missing_frame_counter > (MISSING_FRAMES_TIMEOUT * cnt->conf.frame_limit) - */ - } else { - - if (debug_level >= CAMERA_VERBOSE) - motion_log(-1, 0, "%s: vid_return_code %d", __FUNCTION__, vid_return_code); - - /* Netcams that change dimensions while Motion is running will - * require that Motion restarts to reinitialize all the many - * buffers inside Motion. It will be a mess to try and recover any - * other way - */ - if (vid_return_code == NETCAM_RESTART_ERROR) { - motion_log(LOG_ERR, 0, "%s: Restarting Motion thread to reinitialize all " - "image buffers", __FUNCTION__); - /* Break out of main loop terminating thread - * watchdog will start us again - * Set lost_connection flag on */ - - cnt->lost_connection = 1; - break; - } - - /* First missed frame - store timestamp - * Don't reset time when thread restarts*/ - if (cnt->connectionlosttime == 0) - cnt->connectionlosttime = cnt->currenttime; - - /* Increase missing_frame_counter - * The first MISSING_FRAMES_TIMEOUT seconds we copy previous virgin image - * After 30 seconds we put a grey error image in the buffer - * If we still have not yet received the initial image from a camera - * we go straight for the grey error image. - */ - ++cnt->missing_frame_counter; - if (cnt->video_dev >= 0 && - cnt->missing_frame_counter < (MISSING_FRAMES_TIMEOUT * cnt->conf.frame_limit)) { - memcpy(cnt->current_image->image, cnt->imgs.image_virgin, cnt->imgs.size); - } else { - const char *tmpin; - char tmpout[80]; - struct tm tmptime; - cnt->lost_connection = 1; - - if (cnt->video_dev >= 0) - tmpin = "CONNECTION TO CAMERA LOST\\nSINCE %Y-%m-%d %T"; - else - tmpin = "UNABLE TO OPEN VIDEO DEVICE\\nSINCE %Y-%m-%d %T"; - localtime_r(&cnt->connectionlosttime, &tmptime); - memset(cnt->current_image->image, 0x80, cnt->imgs.size); - mystrftime(cnt, tmpout, sizeof(tmpout), tmpin, &tmptime, NULL, 0); - draw_text(cnt->current_image->image, 10, 20 * text_size_factor, cnt->imgs.width, - tmpout, cnt->conf.text_double); - - /* Write error message only once */ - if (cnt->missing_frame_counter == MISSING_FRAMES_TIMEOUT * cnt->conf.frame_limit) { - motion_log(LOG_ERR, 0, "%s: Video signal lost - Adding grey image", - __FUNCTION__); - // Event for lost video signal can be called from here - event(cnt, EVENT_CAMERA_LOST, NULL, NULL, - NULL, cnt->currenttime_tm); - } - - /* If we don't get a valid frame for a long time, try to close/reopen device - * Only try this when a device is open */ - if ( (cnt->video_dev > 0) && - (cnt->missing_frame_counter == (MISSING_FRAMES_TIMEOUT * 4) * cnt->conf.frame_limit) ) { - motion_log(LOG_ERR, 0, "%s: Video signal still lost - " - "Trying to close video device",__FUNCTION__); - vid_close(cnt); - } - } - } - - /***** MOTION LOOP - MOTION DETECTION SECTION *****/ - - /* The actual motion detection takes place in the following - * diffs is the number of pixels detected as changed - * Make a differences picture in image_out - * - * alg_diff_standard is the slower full feature motion detection algorithm - * alg_diff first calls a fast detection algorithm which only looks at a - * fraction of the pixels. If this detects possible motion alg_diff_standard - * is called. - */ - if (cnt->threshold && !cnt->pause) { - /* if we've already detected motion and we want to see if there's - * still motion, don't bother trying the fast one first. IF there's - * motion, the alg_diff will trigger alg_diff_standard - * anyway - */ - if (cnt->detecting_motion || cnt->conf.setup_mode) - cnt->current_image->diffs = alg_diff_standard(cnt, cnt->imgs.image_virgin); - else - cnt->current_image->diffs = alg_diff(cnt, cnt->imgs.image_virgin); - - /* Lightswitch feature - has light intensity changed? - * This can happen due to change of light conditions or due to a sudden change of the camera - * sensitivity. If alg_lightswitch detects lightswitch we suspend motion detection the next - * 5 frames to allow the camera to settle. - * Don't check if we have lost connection, we detect "Lost signal" frame as lightswitch - */ - if (cnt->conf.lightswitch && !cnt->lost_connection) { - if (alg_lightswitch(cnt, cnt->current_image->diffs)) { - if (debug_level >= CAMERA_DEBUG) - motion_log(-1, 0, "%s: Lightswitch detected", __FUNCTION__); - if (cnt->moved < 5) - cnt->moved = 5; - cnt->current_image->diffs = 0; - alg_update_reference_frame(cnt, RESET_REF_FRAME); - } - } - - /* Switchfilter feature tries to detect a change in the video signal - * from one camera to the next. This is normally used in the Round - * Robin feature. The algorithm is not very safe. - * The algorithm takes a little time so we only call it when needed - * ie. when feature is enabled and diffs>threshold. - * We do not suspend motion detection like we did for lightswitch - * because with Round Robin this is controlled by roundrobin_skip. - */ - if (cnt->conf.switchfilter && cnt->current_image->diffs > cnt->threshold) { - cnt->current_image->diffs = alg_switchfilter(cnt, cnt->current_image->diffs, cnt->current_image->image); - if (cnt->current_image->diffs <= cnt->threshold) { - cnt->current_image->diffs = 0; - if (debug_level >= CAMERA_DEBUG) - motion_log(-1, 0, "%s: Switchfilter detected", __FUNCTION__); - } - } - - /* Despeckle feature - * First we run (as given by the despeckle_filter option iterations - * of erode and dilate algorithms. - * Finally we run the labelling feature. - * All this is done in the alg_despeckle code. - */ - cnt->current_image->total_labels = 0; - cnt->imgs.largest_label = 0; - olddiffs = 0; - if (cnt->conf.despeckle_filter && cnt->current_image->diffs > 0) { - olddiffs = cnt->current_image->diffs; - cnt->current_image->diffs = alg_despeckle(cnt, olddiffs); - }else if (cnt->imgs.labelsize_max) - cnt->imgs.labelsize_max = 0; /* Disable labeling if enabled */ - - } else if (!cnt->conf.setup_mode) - cnt->current_image->diffs = 0; - - /* Manipulate smart_mask sensitivity (only every smartmask_ratio seconds) */ - if (cnt->smartmask_speed && (cnt->event_nr != cnt->prev_event)) { - if (!--smartmask_count){ - alg_tune_smartmask(cnt); - smartmask_count = smartmask_ratio; - } - } - - /* cnt->moved is set by the tracking code when camera has been asked to move. - * When camera is moving we do not want motion to detect motion or we will - * get our camera chasing itself like crazy and we will get motion detected - * which is not really motion. So we pretend there is no motion by setting - * cnt->diffs = 0. - * We also pretend to have a moving camera when we start Motion and when light - * switch has been detected to allow camera to settle. - */ - if (cnt->moved) { - cnt->moved--; - cnt->current_image->diffs = 0; - } - - /***** MOTION LOOP - TUNING SECTION *****/ - - /* if noise tuning was selected, do it now. but only when - * no frames have been recorded and only once per second - */ - if (cnt->conf.noise_tune && cnt->shots == 0) { - if (!cnt->detecting_motion && (cnt->current_image->diffs <= cnt->threshold)) - alg_noise_tune(cnt, cnt->imgs.image_virgin); - } - - /* if we are not noise tuning lets make sure that remote controlled - * changes of noise_level are used. - */ - if (!cnt->conf.noise_tune) - cnt->noise = cnt->conf.noise; - - /* threshold tuning if enabled - * if we are not threshold tuning lets make sure that remote controlled - * changes of threshold are used. - */ - if (cnt->conf.threshold_tune) - alg_threshold_tune(cnt, cnt->current_image->diffs, cnt->detecting_motion); - else - cnt->threshold = cnt->conf.max_changes; - - /* If motion is detected (cnt->current_image->diffs > cnt->threshold) and before we add text to the pictures - we find the center and size coordinates of the motion to be used for text overlays and later - for adding the locate rectangle */ - if (cnt->current_image->diffs > cnt->threshold) - alg_locate_center_size(&cnt->imgs, cnt->imgs.width, cnt->imgs.height, &cnt->current_image->location); - - /* Update reference frame. */ - /* micro-lighswitch: e.g. neighbors cat switched on the motion sensitive * - * frontdoor illumination. Updates are rate-limited to 3 per second at * - * framerates above 5fps to save CPU resources and to keep sensitivity * - * at a constant level. * - */ - ref_frame_limit++; - if (ref_frame_limit >= (cnt->lastrate / 3)) { - ref_frame_limit = 0; - if ((cnt->current_image->diffs > cnt->threshold) && - (cnt->lightswitch_framecounter < (cnt->lastrate * 2)) && /* two seconds window */ - ((abs(previous_diffs - cnt->current_image->diffs)) < (previous_diffs / 15)) && - ((abs(cnt->current_image->location.x - previous_location_x)) <= (cnt->imgs.width / 150)) && - ((abs(cnt->current_image->location.y - previous_location_y)) <= (cnt->imgs.height / 150))) { - alg_update_reference_frame(cnt, RESET_REF_FRAME); - cnt->current_image->diffs = 0; - cnt->lightswitch_framecounter = 0; - if (debug_level >= CAMERA_DEBUG) - motion_log(-1, 0, "%s: micro-lightswitch!", __FUNCTION__); - } else { - alg_update_reference_frame(cnt, UPDATE_REF_FRAME); - } - previous_diffs = cnt->current_image->diffs; - previous_location_x = cnt->current_image->location.x; - previous_location_y = cnt->current_image->location.y; - } - - /***** MOTION LOOP - TEXT AND GRAPHICS OVERLAY SECTION *****/ - - /* Some overlays on top of the motion image - * Note that these now modifies the cnt->imgs.out so this buffer - * can no longer be used for motion detection features until next - * picture frame is captured. - */ - - /* Smartmask overlay */ - if (cnt->smartmask_speed && (cnt->conf.motion_img || cnt->conf.ffmpeg_output_debug || cnt->conf.setup_mode) ) - overlay_smartmask(cnt, cnt->imgs.out); - - /* Largest labels overlay */ - if (cnt->imgs.largest_label && (cnt->conf.motion_img || cnt->conf.ffmpeg_output_debug || cnt->conf.setup_mode) ) - overlay_largest_label(cnt, cnt->imgs.out); - - - /* Fixed mask overlay */ - if (cnt->imgs.mask && (cnt->conf.motion_img || cnt->conf.ffmpeg_output_debug || cnt->conf.setup_mode) ) - overlay_fixed_mask(cnt, cnt->imgs.out); - - /* Initialize the double sized characters if needed. */ - if(cnt->conf.text_double && text_size_factor == 1) - text_size_factor = 2; - - /* If text_double is set to off, then reset the scaling text_size_factor. */ - else if(!cnt->conf.text_double && text_size_factor == 2) { - text_size_factor = 1; - } - - /* Add changed pixels in upper right corner of the pictures */ - if (cnt->conf.text_changes) { - char tmp[15]; - - if (!cnt->pause) - sprintf(tmp, "%d", cnt->current_image->diffs); - else - sprintf(tmp, "-"); - - draw_text(cnt->current_image->image, cnt->imgs.width - 10, 10, cnt->imgs.width, tmp, cnt->conf.text_double); - } - - /* Add changed pixels to motion-images (for stream) in setup_mode - and always overlay smartmask (not only when motion is detected) */ - if (debug_level >= CAMERA_VERBOSE) { - char tmp[PATH_MAX]; - sprintf(tmp, "D:%5d L:%3d N:%3d", cnt->current_image->diffs, cnt->current_image->total_labels, cnt->noise); - draw_text(cnt->imgs.out, cnt->imgs.width - 10, cnt->imgs.height - 30 * text_size_factor, - cnt->imgs.width, tmp, cnt->conf.text_double); - sprintf(tmp, "THREAD %d SETUP", cnt->threadnr); - draw_text(cnt->imgs.out, cnt->imgs.width - 10, cnt->imgs.height - 10 * text_size_factor, - cnt->imgs.width, tmp, cnt->conf.text_double); - } - - /* Add text in lower left corner of the pictures */ - if (cnt->conf.text_left) { - char tmp[PATH_MAX]; - mystrftime(cnt, tmp, sizeof(tmp), cnt->conf.text_left, &cnt->current_image->timestamp_tm, NULL, 0); - draw_text(cnt->current_image->image, 10, cnt->imgs.height - 10 * text_size_factor, cnt->imgs.width, - tmp, cnt->conf.text_double); - } - - /* Add text in lower right corner of the pictures */ - if (cnt->conf.text_right) { - char tmp[PATH_MAX]; - mystrftime(cnt, tmp, sizeof(tmp), cnt->conf.text_right, &cnt->current_image->timestamp_tm, NULL, 0); - draw_text(cnt->current_image->image, cnt->imgs.width - 10, cnt->imgs.height - 10 * text_size_factor, - cnt->imgs.width, tmp, cnt->conf.text_double); - } - - - /***** MOTION LOOP - ACTIONS AND EVENT CONTROL SECTION *****/ - - if (cnt->current_image->diffs > cnt->threshold) { - /* flag this image, it have motion */ - cnt->current_image->flags |= IMAGE_MOTION; - cnt->lightswitch_framecounter++; /* micro lightswitch */ - } else cnt->lightswitch_framecounter = 0; - - /* If motion has been detected we take action and start saving - * pictures and movies etc by calling motion_detected(). - * Is emulate_motion enabled we always call motion_detected() - * If post_capture is enabled we also take care of this in the this - * code section. - */ - if ( cnt->conf.emulate_motion && (cnt->startup_frames == 0) ) { - cnt->detecting_motion = 1; - /* Setup the postcap counter */ - cnt->postcap = cnt->conf.post_capture; - cnt->current_image->flags |= (IMAGE_TRIGGER | IMAGE_SAVE); - motion_detected(cnt, cnt->video_dev, cnt->current_image); - } else if ( (cnt->current_image->flags & IMAGE_MOTION) && (cnt->startup_frames == 0) ) { - /* Did we detect motion (like the cat just walked in :) )? - * If so, ensure the motion is sustained if minimum_motion_frames - */ - - /* Count how many frames with motion there is in the last minimum_motion_frames in precap buffer */ - int frame_count = 0; - int pos = cnt->imgs.image_ring_in; - - for(i = 0; i < cnt->conf.minimum_motion_frames; i++) - { - if(cnt->imgs.image_ring[pos].flags & IMAGE_MOTION) - frame_count++; - if (pos == 0) { - pos = cnt->imgs.image_ring_size-1; - } else { - pos--; - } - } - - if (frame_count >= cnt->conf.minimum_motion_frames) { - cnt->current_image->flags |= (IMAGE_TRIGGER | IMAGE_SAVE); - cnt->detecting_motion = 1; - /* Setup the postcap counter */ - cnt->postcap = cnt->conf.post_capture; - /* Mark all images in image_ring to be saved */ - for(i = 0; i < cnt->imgs.image_ring_size; i++) { - cnt->imgs.image_ring[i].flags |= IMAGE_SAVE; - } - } - else if (cnt->postcap) { /* we have motion in this frame, but not enought frames for trigger. Check postcap */ - cnt->current_image->flags |= (IMAGE_POSTCAP | IMAGE_SAVE); - cnt->postcap--; - } else { - cnt->current_image->flags |= IMAGE_PRECAP; - } - - /* Always call motion_detected when we have a motion image */ - motion_detected(cnt, cnt->video_dev, cnt->current_image); - } else if (cnt->postcap) { - /* No motion, doing postcap */ - cnt->current_image->flags |= (IMAGE_POSTCAP | IMAGE_SAVE); - cnt->postcap--; - } else { - /* Done with postcap, so just have the image in the precap buffer */ - cnt->current_image->flags |= IMAGE_PRECAP; - cnt->detecting_motion = 0; - } - - /* Update last frame saved time, so we can end event after gap time */ - if (cnt->current_image->flags & IMAGE_SAVE) { - cnt->lasttime = cnt->current_image->timestamp; - } - - /* Simple hack to recognize motion in a specific area */ - /* Do we need a new coversion specifier as well?? */ - if ((cnt->conf.area_detect) && (cnt->event_nr != area_once) && (cnt->current_image->flags & IMAGE_TRIGGER)) { - j = strlen(cnt->conf.area_detect); - for (i = 0; i < j; i++) { - z = cnt->conf.area_detect[i] - 49; /* 1 becomes 0 */ - if ((z >= 0) && (z < 9)) { - if (cnt->current_image->location.x > area_minx[z] && - cnt->current_image->location.x < area_maxx[z] && - cnt->current_image->location.y > area_miny[z] && - cnt->current_image->location.y < area_maxy[z]) { - event(cnt, EVENT_AREA_DETECTED, NULL, NULL, - NULL, cnt->currenttime_tm); - area_once = cnt->event_nr; /* Fire script only once per event */ - if (debug_level >= CAMERA_DEBUG) - motion_log(-1, 0, "Motion in area %d detected.\n", z+1); - break; - } - } - } - } - - /* Is the movie too long? Then make movies - * First test for max_movie_time - */ - if (cnt->conf.max_movie_time && cnt->event_nr == cnt->prev_event) - if (cnt->currenttime - cnt->eventtime >= cnt->conf.max_movie_time) - cnt->makemovie = 1; - - /* Now test for quiet longer than 'gap' OR make movie as decided in - * previous statement. - */ - if (((cnt->currenttime - cnt->lasttime >= cnt->conf.event_gap) && cnt->conf.event_gap > 0) || cnt->makemovie) { - if (cnt->event_nr == cnt->prev_event || cnt->makemovie) { - - /* Flush image buffer */ - process_image_ring(cnt, IMAGE_BUFFER_FLUSH); - - /* Save preview_shot here at the end of event */ - if (cnt->imgs.preview_image.diffs) { - preview_save(cnt); - cnt->imgs.preview_image.diffs = 0; - } - - event(cnt, EVENT_ENDMOTION, NULL, NULL, NULL, cnt->currenttime_tm); - - /* if tracking is enabled we center our camera so it does not - * point to a place where it will miss the next action - */ - if (cnt->track.type) - cnt->moved = track_center(cnt, cnt->video_dev, 0, 0, 0); - - if (debug_level >= CAMERA_DEBUG) - motion_log(-1, 0, "%s: End of event %d", - __FUNCTION__, cnt->event_nr); - - cnt->makemovie = 0; - /* Reset post capture */ - cnt->postcap = 0; - - /* Finally we increase the event number */ - cnt->event_nr++; - cnt->lightswitch_framecounter = 0; - - /* And we unset the text_event_string to avoid that buffered - * images get a timestamp from previous event. - */ - cnt->text_event_string[0] = '\0'; - } - } - - /* Save/send to movie some images */ - process_image_ring(cnt, 2); - - /***** MOTION LOOP - SETUP MODE CONSOLE OUTPUT SECTION *****/ - - /* If CAMERA_VERBOSE enabled output some numbers to console */ - if (debug_level >= CAMERA_VERBOSE){ - char msg[1024] = "\0"; - char part[100]; - - if (cnt->conf.despeckle_filter) { - snprintf(part, 99, "Raw changes: %5d - changes after '%s': %5d", - olddiffs, cnt->conf.despeckle_filter, cnt->current_image->diffs); - strcat(msg, part); - if (strchr(cnt->conf.despeckle_filter, 'l')){ - sprintf(part, " - labels: %3d", cnt->current_image->total_labels); - strcat(msg, part); - } - } - else{ - sprintf(part, "Changes: %5d", cnt->current_image->diffs); - strcat(msg, part); - } - if (cnt->conf.noise_tune){ - sprintf(part, " - noise level: %2d", cnt->noise); - strcat(msg, part); - } - if (cnt->conf.threshold_tune){ - sprintf(part, " - threshold: %d", cnt->threshold); - strcat(msg, part); - } - motion_log(-1, 0, "%s", msg); - } - - } /* get_image end */ - - /***** MOTION LOOP - SNAPSHOT FEATURE SECTION *****/ - - /* Did we get triggered to make a snapshot from control http? Then shoot a snap - * If snapshot_interval is not zero and time since epoch MOD snapshot_interval = 0 then snap - * We actually allow the time to run over the interval in case we have a delay - * from slow camera. - * Note: Negative value means SIGALRM snaps are enabled - * httpd-control snaps are always enabled. - */ - - /* time_current_frame is used both for snapshot and timelapse features */ - time_current_frame = cnt->currenttime; - - if ( (cnt->conf.snapshot_interval > 0 && cnt->shots == 0 && - time_current_frame % cnt->conf.snapshot_interval <= time_last_frame % cnt->conf.snapshot_interval) || - cnt->snapshot) { - event(cnt, EVENT_IMAGE_SNAPSHOT, cnt->current_image->image, NULL, NULL, &cnt->current_image->timestamp_tm); - cnt->snapshot = 0; - } - - - /***** MOTION LOOP - TIMELAPSE FEATURE SECTION *****/ + /* save the newly captured still virgin image to a buffer + * which we will not alter with text and location graphics + */ + memcpy(cnt->imgs.image_virgin, cnt->current_image->image, cnt->imgs.size); + + /* If the camera is a netcam we let the camera decide the pace. + * Otherwise we will keep on adding duplicate frames. + * By resetting the timer the framerate becomes maximum the rate + * of the Netcam. + */ + if (cnt->conf.netcam_url) { + gettimeofday(&tv1, NULL); + timenow = tv1.tv_usec + 1000000L * tv1.tv_sec; + } + // FATAL ERROR - leave the thread by breaking out of the main loop + } else if (vid_return_code < 0) { + /* Fatal error - Close video device */ + motion_log(LOG_ERR, 0, "%s: Video device fatal error - Closing video device", + __FUNCTION__); + vid_close(cnt); + /* Use virgin image, if we are not able to open it again next loop + * a gray image with message is applied + * flag lost_connection + */ + memcpy(cnt->current_image->image, cnt->imgs.image_virgin, cnt->imgs.size); + cnt->lost_connection = 1; + /* NO FATAL ERROR - + * copy last image or show grey image with message + * flag on lost_connection if : + * vid_return_code == NETCAM_RESTART_ERROR + * cnt->video_dev < 0 + * cnt->missing_frame_counter > (MISSING_FRAMES_TIMEOUT * cnt->conf.frame_limit) + */ + } else { + + if (debug_level >= CAMERA_VERBOSE) + motion_log(-1, 0, "%s: vid_return_code %d", __FUNCTION__, vid_return_code); + + /* Netcams that change dimensions while Motion is running will + * require that Motion restarts to reinitialize all the many + * buffers inside Motion. It will be a mess to try and recover any + * other way + */ + if (vid_return_code == NETCAM_RESTART_ERROR) { + motion_log(LOG_ERR, 0, "%s: Restarting Motion thread to reinitialize all " + "image buffers", __FUNCTION__); + /* Break out of main loop terminating thread + * watchdog will start us again + * Set lost_connection flag on */ + + cnt->lost_connection = 1; + break; + } + + /* First missed frame - store timestamp + * Don't reset time when thread restarts*/ + if (cnt->connectionlosttime == 0) + cnt->connectionlosttime = cnt->currenttime; + + /* Increase missing_frame_counter + * The first MISSING_FRAMES_TIMEOUT seconds we copy previous virgin image + * After 30 seconds we put a grey error image in the buffer + * If we still have not yet received the initial image from a camera + * we go straight for the grey error image. + */ + ++cnt->missing_frame_counter; + + if (cnt->video_dev >= 0 && + cnt->missing_frame_counter < (MISSING_FRAMES_TIMEOUT * cnt->conf.frame_limit)) { + memcpy(cnt->current_image->image, cnt->imgs.image_virgin, cnt->imgs.size); + } else { + const char *tmpin; + char tmpout[80]; + struct tm tmptime; + cnt->lost_connection = 1; + + if (cnt->video_dev >= 0) + tmpin = "CONNECTION TO CAMERA LOST\\nSINCE %Y-%m-%d %T"; + else + tmpin = "UNABLE TO OPEN VIDEO DEVICE\\nSINCE %Y-%m-%d %T"; + localtime_r(&cnt->connectionlosttime, &tmptime); + memset(cnt->current_image->image, 0x80, cnt->imgs.size); + mystrftime(cnt, tmpout, sizeof(tmpout), tmpin, &tmptime, NULL, 0); + draw_text(cnt->current_image->image, 10, 20 * text_size_factor, cnt->imgs.width, + tmpout, cnt->conf.text_double); + + /* Write error message only once */ + if (cnt->missing_frame_counter == MISSING_FRAMES_TIMEOUT * cnt->conf.frame_limit) { + motion_log(LOG_ERR, 0, "%s: Video signal lost - Adding grey image", + __FUNCTION__); + // Event for lost video signal can be called from here + event(cnt, EVENT_CAMERA_LOST, NULL, NULL, + NULL, cnt->currenttime_tm); + } + + /* If we don't get a valid frame for a long time, try to close/reopen device + * Only try this when a device is open */ + if ((cnt->video_dev > 0) && + (cnt->missing_frame_counter == (MISSING_FRAMES_TIMEOUT * 4) * cnt->conf.frame_limit)) { + motion_log(LOG_ERR, 0, "%s: Video signal still lost - " + "Trying to close video device",__FUNCTION__); + vid_close(cnt); + } + } + } + + /***** MOTION LOOP - MOTION DETECTION SECTION *****/ + + /* The actual motion detection takes place in the following + * diffs is the number of pixels detected as changed + * Make a differences picture in image_out + * + * alg_diff_standard is the slower full feature motion detection algorithm + * alg_diff first calls a fast detection algorithm which only looks at a + * fraction of the pixels. If this detects possible motion alg_diff_standard + * is called. + */ + if (cnt->threshold && !cnt->pause) { + /* if we've already detected motion and we want to see if there's + * still motion, don't bother trying the fast one first. IF there's + * motion, the alg_diff will trigger alg_diff_standard + * anyway + */ + if (cnt->detecting_motion || cnt->conf.setup_mode) + cnt->current_image->diffs = alg_diff_standard(cnt, cnt->imgs.image_virgin); + else + cnt->current_image->diffs = alg_diff(cnt, cnt->imgs.image_virgin); + + /* Lightswitch feature - has light intensity changed? + * This can happen due to change of light conditions or due to a sudden change of the camera + * sensitivity. If alg_lightswitch detects lightswitch we suspend motion detection the next + * 5 frames to allow the camera to settle. + * Don't check if we have lost connection, we detect "Lost signal" frame as lightswitch + */ + if (cnt->conf.lightswitch && !cnt->lost_connection) { + if (alg_lightswitch(cnt, cnt->current_image->diffs)) { + if (debug_level >= CAMERA_DEBUG) + motion_log(-1, 0, "%s: Lightswitch detected", __FUNCTION__); + + if (cnt->moved < 5) + cnt->moved = 5; + + cnt->current_image->diffs = 0; + alg_update_reference_frame(cnt, RESET_REF_FRAME); + } + } + + /* Switchfilter feature tries to detect a change in the video signal + * from one camera to the next. This is normally used in the Round + * Robin feature. The algorithm is not very safe. + * The algorithm takes a little time so we only call it when needed + * ie. when feature is enabled and diffs>threshold. + * We do not suspend motion detection like we did for lightswitch + * because with Round Robin this is controlled by roundrobin_skip. + */ + if (cnt->conf.switchfilter && cnt->current_image->diffs > cnt->threshold) { + cnt->current_image->diffs = alg_switchfilter(cnt, cnt->current_image->diffs, cnt->current_image->image); + + if (cnt->current_image->diffs <= cnt->threshold) { + cnt->current_image->diffs = 0; + + if (debug_level >= CAMERA_DEBUG) + motion_log(-1, 0, "%s: Switchfilter detected", __FUNCTION__); + } + } + + /* Despeckle feature + * First we run (as given by the despeckle_filter option iterations + * of erode and dilate algorithms. + * Finally we run the labelling feature. + * All this is done in the alg_despeckle code. + */ + cnt->current_image->total_labels = 0; + cnt->imgs.largest_label = 0; + olddiffs = 0; + + if (cnt->conf.despeckle_filter && cnt->current_image->diffs > 0) { + olddiffs = cnt->current_image->diffs; + cnt->current_image->diffs = alg_despeckle(cnt, olddiffs); + } else if (cnt->imgs.labelsize_max) { + cnt->imgs.labelsize_max = 0; /* Disable labeling if enabled */ + } + + } else if (!cnt->conf.setup_mode) { + cnt->current_image->diffs = 0; + } + + /* Manipulate smart_mask sensitivity (only every smartmask_ratio seconds) */ + if (cnt->smartmask_speed && (cnt->event_nr != cnt->prev_event)) { + if (!--smartmask_count) { + alg_tune_smartmask(cnt); + smartmask_count = smartmask_ratio; + } + } + + /* cnt->moved is set by the tracking code when camera has been asked to move. + * When camera is moving we do not want motion to detect motion or we will + * get our camera chasing itself like crazy and we will get motion detected + * which is not really motion. So we pretend there is no motion by setting + * cnt->diffs = 0. + * We also pretend to have a moving camera when we start Motion and when light + * switch has been detected to allow camera to settle. + */ + if (cnt->moved) { + cnt->moved--; + cnt->current_image->diffs = 0; + } + + /***** MOTION LOOP - TUNING SECTION *****/ + + /* if noise tuning was selected, do it now. but only when + * no frames have been recorded and only once per second + */ + if (cnt->conf.noise_tune && cnt->shots == 0) { + if (!cnt->detecting_motion && (cnt->current_image->diffs <= cnt->threshold)) + alg_noise_tune(cnt, cnt->imgs.image_virgin); + } + + /* if we are not noise tuning lets make sure that remote controlled + * changes of noise_level are used. + */ + if (!cnt->conf.noise_tune) + cnt->noise = cnt->conf.noise; + + /* threshold tuning if enabled + * if we are not threshold tuning lets make sure that remote controlled + * changes of threshold are used. + */ + if (cnt->conf.threshold_tune) + alg_threshold_tune(cnt, cnt->current_image->diffs, cnt->detecting_motion); + else + cnt->threshold = cnt->conf.max_changes; + + /* If motion is detected (cnt->current_image->diffs > cnt->threshold) and before we add text to the pictures + we find the center and size coordinates of the motion to be used for text overlays and later + for adding the locate rectangle */ + if (cnt->current_image->diffs > cnt->threshold) + alg_locate_center_size(&cnt->imgs, cnt->imgs.width, cnt->imgs.height, &cnt->current_image->location); + + /* Update reference frame. */ + /* micro-lighswitch: e.g. neighbors cat switched on the motion sensitive * + * frontdoor illumination. Updates are rate-limited to 3 per second at * + * framerates above 5fps to save CPU resources and to keep sensitivity * + * at a constant level. * + */ + ref_frame_limit++; + + if (ref_frame_limit >= (cnt->lastrate / 3)) { + ref_frame_limit = 0; + + if ((cnt->current_image->diffs > cnt->threshold) && + (cnt->lightswitch_framecounter < (cnt->lastrate * 2)) && /* two seconds window */ + ((abs(previous_diffs - cnt->current_image->diffs)) < (previous_diffs / 15)) && + ((abs(cnt->current_image->location.x - previous_location_x)) <= (cnt->imgs.width / 150)) && + ((abs(cnt->current_image->location.y - previous_location_y)) <= (cnt->imgs.height / 150))) { + alg_update_reference_frame(cnt, RESET_REF_FRAME); + cnt->current_image->diffs = 0; + cnt->lightswitch_framecounter = 0; + + if (debug_level >= CAMERA_DEBUG) + motion_log(-1, 0, "%s: micro-lightswitch!", __FUNCTION__); + } else { + alg_update_reference_frame(cnt, UPDATE_REF_FRAME); + } + + previous_diffs = cnt->current_image->diffs; + previous_location_x = cnt->current_image->location.x; + previous_location_y = cnt->current_image->location.y; + } + + /***** MOTION LOOP - TEXT AND GRAPHICS OVERLAY SECTION *****/ + + /* Some overlays on top of the motion image + * Note that these now modifies the cnt->imgs.out so this buffer + * can no longer be used for motion detection features until next + * picture frame is captured. + */ + + /* Smartmask overlay */ + if (cnt->smartmask_speed && (cnt->conf.motion_img || cnt->conf.ffmpeg_output_debug || + cnt->conf.setup_mode)) + overlay_smartmask(cnt, cnt->imgs.out); + + /* Largest labels overlay */ + if (cnt->imgs.largest_label && (cnt->conf.motion_img || cnt->conf.ffmpeg_output_debug || + cnt->conf.setup_mode)) + overlay_largest_label(cnt, cnt->imgs.out); + + /* Fixed mask overlay */ + if (cnt->imgs.mask && (cnt->conf.motion_img || cnt->conf.ffmpeg_output_debug || + cnt->conf.setup_mode)) + overlay_fixed_mask(cnt, cnt->imgs.out); + + /* Initialize the double sized characters if needed. */ + if (cnt->conf.text_double && text_size_factor == 1) { + text_size_factor = 2; + /* If text_double is set to off, then reset the scaling text_size_factor. */ + } else if (!cnt->conf.text_double && text_size_factor == 2) { + text_size_factor = 1; + } + + /* Add changed pixels in upper right corner of the pictures */ + if (cnt->conf.text_changes) { + char tmp[15]; + + if (!cnt->pause) + sprintf(tmp, "%d", cnt->current_image->diffs); + else + sprintf(tmp, "-"); + + draw_text(cnt->current_image->image, cnt->imgs.width - 10, 10, + cnt->imgs.width, tmp, cnt->conf.text_double); + } + + /* Add changed pixels to motion-images (for stream) in setup_mode + and always overlay smartmask (not only when motion is detected) */ + if (debug_level >= CAMERA_VERBOSE) { + char tmp[PATH_MAX]; + sprintf(tmp, "D:%5d L:%3d N:%3d", cnt->current_image->diffs, + cnt->current_image->total_labels, cnt->noise); + draw_text(cnt->imgs.out, cnt->imgs.width - 10, cnt->imgs.height - 30 * text_size_factor, + cnt->imgs.width, tmp, cnt->conf.text_double); + sprintf(tmp, "THREAD %d SETUP", cnt->threadnr); + draw_text(cnt->imgs.out, cnt->imgs.width - 10, cnt->imgs.height - 10 * text_size_factor, + cnt->imgs.width, tmp, cnt->conf.text_double); + } + + /* Add text in lower left corner of the pictures */ + if (cnt->conf.text_left) { + char tmp[PATH_MAX]; + mystrftime(cnt, tmp, sizeof(tmp), cnt->conf.text_left, + &cnt->current_image->timestamp_tm, NULL, 0); + draw_text(cnt->current_image->image, 10, cnt->imgs.height - 10 * text_size_factor, + cnt->imgs.width, tmp, cnt->conf.text_double); + } + + /* Add text in lower right corner of the pictures */ + if (cnt->conf.text_right) { + char tmp[PATH_MAX]; + mystrftime(cnt, tmp, sizeof(tmp), cnt->conf.text_right, + &cnt->current_image->timestamp_tm, NULL, 0); + draw_text(cnt->current_image->image, cnt->imgs.width - 10, + cnt->imgs.height - 10 * text_size_factor, + cnt->imgs.width, tmp, cnt->conf.text_double); + } + + + /***** MOTION LOOP - ACTIONS AND EVENT CONTROL SECTION *****/ + + if (cnt->current_image->diffs > cnt->threshold) { + /* flag this image, it have motion */ + cnt->current_image->flags |= IMAGE_MOTION; + cnt->lightswitch_framecounter++; /* micro lightswitch */ + } else { + cnt->lightswitch_framecounter = 0; + } + + /* If motion has been detected we take action and start saving + * pictures and movies etc by calling motion_detected(). + * Is emulate_motion enabled we always call motion_detected() + * If post_capture is enabled we also take care of this in the this + * code section. + */ + if (cnt->conf.emulate_motion && (cnt->startup_frames == 0)) { + cnt->detecting_motion = 1; + /* Setup the postcap counter */ + cnt->postcap = cnt->conf.post_capture; + cnt->current_image->flags |= (IMAGE_TRIGGER | IMAGE_SAVE); + motion_detected(cnt, cnt->video_dev, cnt->current_image); + } else if ((cnt->current_image->flags & IMAGE_MOTION) && (cnt->startup_frames == 0)) { + /* Did we detect motion (like the cat just walked in :) )? + * If so, ensure the motion is sustained if minimum_motion_frames + */ + + /* Count how many frames with motion there is in the last minimum_motion_frames in precap buffer */ + int frame_count = 0; + int pos = cnt->imgs.image_ring_in; + + for (i = 0; i < cnt->conf.minimum_motion_frames; i++) { + + if (cnt->imgs.image_ring[pos].flags & IMAGE_MOTION) + frame_count++; + + if (pos == 0) + pos = cnt->imgs.image_ring_size-1; + else + pos--; + + } + + if (frame_count >= cnt->conf.minimum_motion_frames) { + cnt->current_image->flags |= (IMAGE_TRIGGER | IMAGE_SAVE); + cnt->detecting_motion = 1; + /* Setup the postcap counter */ + cnt->postcap = cnt->conf.post_capture; + /* Mark all images in image_ring to be saved */ + for (i = 0; i < cnt->imgs.image_ring_size; i++) + cnt->imgs.image_ring[i].flags |= IMAGE_SAVE; + + } else if (cnt->postcap) { /* we have motion in this frame, + but not enought frames for trigger. Check postcap */ + cnt->current_image->flags |= (IMAGE_POSTCAP | IMAGE_SAVE); + cnt->postcap--; + } else { + cnt->current_image->flags |= IMAGE_PRECAP; + } + + /* Always call motion_detected when we have a motion image */ + motion_detected(cnt, cnt->video_dev, cnt->current_image); + } else if (cnt->postcap) { + /* No motion, doing postcap */ + cnt->current_image->flags |= (IMAGE_POSTCAP | IMAGE_SAVE); + cnt->postcap--; + } else { + /* Done with postcap, so just have the image in the precap buffer */ + cnt->current_image->flags |= IMAGE_PRECAP; + cnt->detecting_motion = 0; + } + + /* Update last frame saved time, so we can end event after gap time */ + if (cnt->current_image->flags & IMAGE_SAVE) + cnt->lasttime = cnt->current_image->timestamp; + + + /* Simple hack to recognize motion in a specific area */ + /* Do we need a new coversion specifier as well?? */ + if ((cnt->conf.area_detect) && (cnt->event_nr != area_once) && (cnt->current_image->flags & IMAGE_TRIGGER)) { + j = strlen(cnt->conf.area_detect); + + for (i = 0; i < j; i++) { + z = cnt->conf.area_detect[i] - 49; /* 1 becomes 0 */ + if ((z >= 0) && (z < 9)) { + if (cnt->current_image->location.x > area_minx[z] && + cnt->current_image->location.x < area_maxx[z] && + cnt->current_image->location.y > area_miny[z] && + cnt->current_image->location.y < area_maxy[z]) { + event(cnt, EVENT_AREA_DETECTED, NULL, NULL, + NULL, cnt->currenttime_tm); + area_once = cnt->event_nr; /* Fire script only once per event */ + + if (debug_level >= CAMERA_DEBUG) + motion_log(-1, 0, "Motion in area %d detected.\n", z+1); + break; + } + } + } + } + + /* Is the movie too long? Then make movies + * First test for max_movie_time + */ + if (cnt->conf.max_movie_time && cnt->event_nr == cnt->prev_event) + if (cnt->currenttime - cnt->eventtime >= cnt->conf.max_movie_time) + cnt->makemovie = 1; + + /* Now test for quiet longer than 'gap' OR make movie as decided in + * previous statement. + */ + if (((cnt->currenttime - cnt->lasttime >= cnt->conf.event_gap) && cnt->conf.event_gap > 0) || + cnt->makemovie) { + if (cnt->event_nr == cnt->prev_event || cnt->makemovie) { + + /* Flush image buffer */ + process_image_ring(cnt, IMAGE_BUFFER_FLUSH); + + /* Save preview_shot here at the end of event */ + if (cnt->imgs.preview_image.diffs) { + preview_save(cnt); + cnt->imgs.preview_image.diffs = 0; + } + + event(cnt, EVENT_ENDMOTION, NULL, NULL, NULL, cnt->currenttime_tm); + + /* if tracking is enabled we center our camera so it does not + * point to a place where it will miss the next action + */ + if (cnt->track.type) + cnt->moved = track_center(cnt, cnt->video_dev, 0, 0, 0); + + if (debug_level >= CAMERA_DEBUG) + motion_log(-1, 0, "%s: End of event %d", + __FUNCTION__, cnt->event_nr); + + cnt->makemovie = 0; + /* Reset post capture */ + cnt->postcap = 0; + + /* Finally we increase the event number */ + cnt->event_nr++; + cnt->lightswitch_framecounter = 0; + + /* And we unset the text_event_string to avoid that buffered + * images get a timestamp from previous event. + */ + cnt->text_event_string[0] = '\0'; + } + } + + /* Save/send to movie some images */ + process_image_ring(cnt, 2); + + /***** MOTION LOOP - SETUP MODE CONSOLE OUTPUT SECTION *****/ + + /* If CAMERA_VERBOSE enabled output some numbers to console */ + if (debug_level >= CAMERA_VERBOSE) { + char msg[1024] = "\0"; + char part[100]; + + if (cnt->conf.despeckle_filter) { + snprintf(part, 99, "Raw changes: %5d - changes after '%s': %5d", + olddiffs, cnt->conf.despeckle_filter, cnt->current_image->diffs); + strcat(msg, part); + if (strchr(cnt->conf.despeckle_filter, 'l')) { + sprintf(part, " - labels: %3d", cnt->current_image->total_labels); + strcat(msg, part); + } + } else { + sprintf(part, "Changes: %5d", cnt->current_image->diffs); + strcat(msg, part); + } + + if (cnt->conf.noise_tune) { + sprintf(part, " - noise level: %2d", cnt->noise); + strcat(msg, part); + } + + if (cnt->conf.threshold_tune) { + sprintf(part, " - threshold: %d", cnt->threshold); + strcat(msg, part); + } + + motion_log(-1, 0, "%s", msg); + } + + } /* get_image end */ + + /***** MOTION LOOP - SNAPSHOT FEATURE SECTION *****/ + + /* Did we get triggered to make a snapshot from control http? Then shoot a snap + * If snapshot_interval is not zero and time since epoch MOD snapshot_interval = 0 then snap + * We actually allow the time to run over the interval in case we have a delay + * from slow camera. + * Note: Negative value means SIGALRM snaps are enabled + * httpd-control snaps are always enabled. + */ + + /* time_current_frame is used both for snapshot and timelapse features */ + time_current_frame = cnt->currenttime; + + if ((cnt->conf.snapshot_interval > 0 && cnt->shots == 0 && + time_current_frame % cnt->conf.snapshot_interval <= time_last_frame % cnt->conf.snapshot_interval) || + cnt->snapshot) { + event(cnt, EVENT_IMAGE_SNAPSHOT, cnt->current_image->image, NULL, NULL, &cnt->current_image->timestamp_tm); + cnt->snapshot = 0; + } + + + /***** MOTION LOOP - TIMELAPSE FEATURE SECTION *****/ #ifdef HAVE_FFMPEG - if (cnt->conf.timelapse) { - - /* Check to see if we should start a new timelapse file. We start one when - * we are on the first shot, and and the seconds are zero. We must use the seconds - * to prevent the timelapse file from getting reset multiple times during the minute. - */ - if (cnt->current_image->timestamp_tm.tm_min == 0 && - (time_current_frame % 60 < time_last_frame % 60) && - cnt->shots == 0) { - - if (strcasecmp(cnt->conf.timelapse_mode, "manual") == 0) - ;/* No action */ - - /* If we are daily, raise timelapseend event at midnight */ - else if (strcasecmp(cnt->conf.timelapse_mode, "daily") == 0) { - if (cnt->current_image->timestamp_tm.tm_hour == 0) - event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tm); - } - - /* handle the hourly case */ - else if (strcasecmp(cnt->conf.timelapse_mode, "hourly") == 0) { - event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tm); - } - - /* If we are weekly-sunday, raise timelapseend event at midnight on sunday */ - else if (strcasecmp(cnt->conf.timelapse_mode, "weekly-sunday") == 0) { - if (cnt->current_image->timestamp_tm.tm_wday == 0 && cnt->current_image->timestamp_tm.tm_hour == 0) - event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tm); - } - - /* If we are weekly-monday, raise timelapseend event at midnight on monday */ - else if (strcasecmp(cnt->conf.timelapse_mode, "weekly-monday") == 0) { - if (cnt->current_image->timestamp_tm.tm_wday == 1 && cnt->current_image->timestamp_tm.tm_hour == 0) - event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tm); - } - - /* If we are monthly, raise timelapseend event at midnight on first day of month */ - else if (strcasecmp(cnt->conf.timelapse_mode, "monthly") == 0) { - if (cnt->current_image->timestamp_tm.tm_mday == 1 && cnt->current_image->timestamp_tm.tm_hour == 0) - event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tm); - } - - /* If invalid we report in syslog once and continue in manual mode */ - else { - motion_log(LOG_ERR, 0, "%s: Invalid timelapse_mode argument '%s'", - __FUNCTION__, cnt->conf.timelapse_mode); - motion_log(LOG_ERR, 0, "%:s Defaulting to manual timelapse mode", - __FUNCTION__); - conf_cmdparse(&cnt, (char *)"ffmpeg_timelapse_mode",(char *)"manual"); - } - } - - /* If ffmpeg timelapse is enabled and time since epoch MOD ffmpeg_timelaps = 0 - * add a timelapse frame to the timelapse movie. - */ - if (cnt->shots == 0 && - time_current_frame % cnt->conf.timelapse <= time_last_frame % cnt->conf.timelapse) - event(cnt, EVENT_TIMELAPSE, cnt->current_image->image, NULL, NULL, &cnt->current_image->timestamp_tm); - } - - /* if timelapse movie is in progress but conf.timelapse is zero then close timelapse file - * This is an important feature that allows manual roll-over of timelapse file using the http - * remote control via a cron job. - */ - else if (cnt->ffmpeg_timelapse) - event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, cnt->currenttime_tm); + if (cnt->conf.timelapse) { + + /* Check to see if we should start a new timelapse file. We start one when + * we are on the first shot, and and the seconds are zero. We must use the seconds + * to prevent the timelapse file from getting reset multiple times during the minute. + */ + if (cnt->current_image->timestamp_tm.tm_min == 0 && + (time_current_frame % 60 < time_last_frame % 60) && + cnt->shots == 0) { + + if (strcasecmp(cnt->conf.timelapse_mode, "manual") == 0) + ;/* No action */ + + /* If we are daily, raise timelapseend event at midnight */ + else if (strcasecmp(cnt->conf.timelapse_mode, "daily") == 0) { + if (cnt->current_image->timestamp_tm.tm_hour == 0) + event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tm); + } + + /* handle the hourly case */ + else if (strcasecmp(cnt->conf.timelapse_mode, "hourly") == 0) { + event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tm); + } + + /* If we are weekly-sunday, raise timelapseend event at midnight on sunday */ + else if (strcasecmp(cnt->conf.timelapse_mode, "weekly-sunday") == 0) { + if (cnt->current_image->timestamp_tm.tm_wday == 0 && + cnt->current_image->timestamp_tm.tm_hour == 0) + event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, + &cnt->current_image->timestamp_tm); + } + + /* If we are weekly-monday, raise timelapseend event at midnight on monday */ + else if (strcasecmp(cnt->conf.timelapse_mode, "weekly-monday") == 0) { + if (cnt->current_image->timestamp_tm.tm_wday == 1 && + cnt->current_image->timestamp_tm.tm_hour == 0) + event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, + &cnt->current_image->timestamp_tm); + } + + /* If we are monthly, raise timelapseend event at midnight on first day of month */ + else if (strcasecmp(cnt->conf.timelapse_mode, "monthly") == 0) { + if (cnt->current_image->timestamp_tm.tm_mday == 1 && + cnt->current_image->timestamp_tm.tm_hour == 0) + event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, + &cnt->current_image->timestamp_tm); + } + + /* If invalid we report in syslog once and continue in manual mode */ + else { + motion_log(LOG_ERR, 0, "%s: Invalid timelapse_mode argument '%s'", + __FUNCTION__, cnt->conf.timelapse_mode); + motion_log(LOG_ERR, 0, "%:s Defaulting to manual timelapse mode", + __FUNCTION__); + conf_cmdparse(&cnt, (char *)"ffmpeg_timelapse_mode",(char *)"manual"); + } + } + + /* If ffmpeg timelapse is enabled and time since epoch MOD ffmpeg_timelaps = 0 + * add a timelapse frame to the timelapse movie. + */ + if (cnt->shots == 0 && time_current_frame % cnt->conf.timelapse <= + time_last_frame % cnt->conf.timelapse) + event(cnt, EVENT_TIMELAPSE, cnt->current_image->image, NULL, NULL, + &cnt->current_image->timestamp_tm); + } + + /* if timelapse movie is in progress but conf.timelapse is zero then close timelapse file + * This is an important feature that allows manual roll-over of timelapse file using the http + * remote control via a cron job. + */ + else if (cnt->ffmpeg_timelapse) + event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, cnt->currenttime_tm); #endif /* HAVE_FFMPEG */ - time_last_frame = time_current_frame; - - - /***** MOTION LOOP - VIDEO LOOPBACK SECTION *****/ - - /* feed last image and motion image to video device pipes and the stream clients - * In setup mode we send the special setup mode image to both stream and vloopback pipe - * In normal mode we feed the latest image to vloopback device and we send - * the image to the stream. We always send the first image in a second to the stream. - * Other image are sent only when the config option stream_motion is off - * The result is that with stream_motion on the stream stream is normally at the minimal - * 1 frame per second but the minute motion is detected the motion_detected() function - * sends all detected pictures to the stream except the 1st per second which is already sent. - */ - if (cnt->conf.setup_mode) { - event(cnt, EVENT_IMAGE, cnt->imgs.out, NULL, &cnt->pipe, cnt->currenttime_tm); - event(cnt, EVENT_STREAM, cnt->imgs.out, NULL, NULL, cnt->currenttime_tm); - } else { - event(cnt, EVENT_IMAGE, cnt->current_image->image, NULL, &cnt->pipe, &cnt->current_image->timestamp_tm); - if (!cnt->conf.stream_motion || cnt->shots == 1) - event(cnt, EVENT_STREAM, cnt->current_image->image, NULL, NULL, &cnt->current_image->timestamp_tm); - } - - event(cnt, EVENT_IMAGEM, cnt->imgs.out, NULL, &cnt->mpipe, cnt->currenttime_tm); - - - /***** MOTION LOOP - ONCE PER SECOND PARAMETER UPDATE SECTION *****/ - - /* Check for some config parameter changes but only every second */ - if (cnt->shots == 0){ - if (strcasecmp(cnt->conf.output_pictures, "on") == 0) - cnt->new_img = NEWIMG_ON; - else if (strcasecmp(cnt->conf.output_pictures, "first") == 0) - cnt->new_img = NEWIMG_FIRST; - else if (strcasecmp(cnt->conf.output_pictures, "best") == 0) - cnt->new_img = NEWIMG_BEST; - else if (strcasecmp(cnt->conf.output_pictures, "center") == 0) - cnt->new_img = NEWIMG_CENTER; - else - cnt->new_img = NEWIMG_OFF; - - if (strcasecmp(cnt->conf.locate_motion, "on") == 0) - cnt->locate_motion = LOCATE_ON; - else if (strcasecmp(cnt->conf.locate_motion, "redbox") == 0) - cnt->locate_motion = LOCATE_REDBOX; - else if (strcasecmp(cnt->conf.locate_motion, "center") == 0) - cnt->locate_motion = LOCATE_CENTER; - else if (strcasecmp(cnt->conf.locate_motion, "redcross") == 0) - cnt->locate_motion = LOCATE_REDCROSS; - else if (strcasecmp(cnt->conf.locate_motion, "preview") == 0) - cnt->locate_motion = LOCATE_PREVIEW; - else - cnt->locate_motion = LOCATE_OFF; - - /* Sanity check for smart_mask_speed, silly value disables smart mask */ - if (cnt->conf.smart_mask_speed < 0 || cnt->conf.smart_mask_speed > 10) - cnt->conf.smart_mask_speed = 0; - /* Has someone changed smart_mask_speed or framerate? */ - if (cnt->conf.smart_mask_speed != cnt->smartmask_speed || smartmask_lastrate != cnt->lastrate){ - if (cnt->conf.smart_mask_speed == 0){ - memset(cnt->imgs.smartmask, 0, cnt->imgs.motionsize); - memset(cnt->imgs.smartmask_final, 255, cnt->imgs.motionsize); - } - smartmask_lastrate = cnt->lastrate; - cnt->smartmask_speed = cnt->conf.smart_mask_speed; - /* Decay delay - based on smart_mask_speed (framerate independent) - This is always 5*smartmask_speed seconds */ - smartmask_ratio = 5 * cnt->lastrate * (11 - cnt->smartmask_speed); - } + time_last_frame = time_current_frame; + + + /***** MOTION LOOP - VIDEO LOOPBACK SECTION *****/ + + /* feed last image and motion image to video device pipes and the stream clients + * In setup mode we send the special setup mode image to both stream and vloopback pipe + * In normal mode we feed the latest image to vloopback device and we send + * the image to the stream. We always send the first image in a second to the stream. + * Other image are sent only when the config option stream_motion is off + * The result is that with stream_motion on the stream stream is normally at the minimal + * 1 frame per second but the minute motion is detected the motion_detected() function + * sends all detected pictures to the stream except the 1st per second which is already sent. + */ + if (cnt->conf.setup_mode) { + event(cnt, EVENT_IMAGE, cnt->imgs.out, NULL, &cnt->pipe, cnt->currenttime_tm); + event(cnt, EVENT_STREAM, cnt->imgs.out, NULL, NULL, cnt->currenttime_tm); + } else { + event(cnt, EVENT_IMAGE, cnt->current_image->image, NULL, + &cnt->pipe, &cnt->current_image->timestamp_tm); + + if (!cnt->conf.stream_motion || cnt->shots == 1) + event(cnt, EVENT_STREAM, cnt->current_image->image, NULL, NULL, + &cnt->current_image->timestamp_tm); + } + + event(cnt, EVENT_IMAGEM, cnt->imgs.out, NULL, &cnt->mpipe, cnt->currenttime_tm); + + + /***** MOTION LOOP - ONCE PER SECOND PARAMETER UPDATE SECTION *****/ + + /* Check for some config parameter changes but only every second */ + if (cnt->shots == 0) { + if (strcasecmp(cnt->conf.output_pictures, "on") == 0) + cnt->new_img = NEWIMG_ON; + else if (strcasecmp(cnt->conf.output_pictures, "first") == 0) + cnt->new_img = NEWIMG_FIRST; + else if (strcasecmp(cnt->conf.output_pictures, "best") == 0) + cnt->new_img = NEWIMG_BEST; + else if (strcasecmp(cnt->conf.output_pictures, "center") == 0) + cnt->new_img = NEWIMG_CENTER; + else + cnt->new_img = NEWIMG_OFF; + + if (strcasecmp(cnt->conf.locate_motion, "on") == 0) + cnt->locate_motion = LOCATE_ON; + else if (strcasecmp(cnt->conf.locate_motion, "redbox") == 0) + cnt->locate_motion = LOCATE_REDBOX; + else if (strcasecmp(cnt->conf.locate_motion, "center") == 0) + cnt->locate_motion = LOCATE_CENTER; + else if (strcasecmp(cnt->conf.locate_motion, "redcross") == 0) + cnt->locate_motion = LOCATE_REDCROSS; + else if (strcasecmp(cnt->conf.locate_motion, "preview") == 0) + cnt->locate_motion = LOCATE_PREVIEW; + else + cnt->locate_motion = LOCATE_OFF; + + /* Sanity check for smart_mask_speed, silly value disables smart mask */ + if (cnt->conf.smart_mask_speed < 0 || cnt->conf.smart_mask_speed > 10) + cnt->conf.smart_mask_speed = 0; + /* Has someone changed smart_mask_speed or framerate? */ + if (cnt->conf.smart_mask_speed != cnt->smartmask_speed || + smartmask_lastrate != cnt->lastrate) { + if (cnt->conf.smart_mask_speed == 0) { + memset(cnt->imgs.smartmask, 0, cnt->imgs.motionsize); + memset(cnt->imgs.smartmask_final, 255, cnt->imgs.motionsize); + } + smartmask_lastrate = cnt->lastrate; + cnt->smartmask_speed = cnt->conf.smart_mask_speed; + /* Decay delay - based on smart_mask_speed (framerate independent) + This is always 5*smartmask_speed seconds */ + smartmask_ratio = 5 * cnt->lastrate * (11 - cnt->smartmask_speed); + } #if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) - /* Set the sql mask file according to the SQL config options - * We update it for every frame in case the config was updated - * via remote control. - */ - cnt->sql_mask = cnt->conf.sql_log_image * (FTYPE_IMAGE + FTYPE_IMAGE_MOTION) + - cnt->conf.sql_log_snapshot * FTYPE_IMAGE_SNAPSHOT + - cnt->conf.sql_log_movie * (FTYPE_MPEG + FTYPE_MPEG_MOTION) + - cnt->conf.sql_log_timelapse * FTYPE_MPEG_TIMELAPSE; + /* Set the sql mask file according to the SQL config options + * We update it for every frame in case the config was updated + * via remote control. + */ + cnt->sql_mask = cnt->conf.sql_log_image * (FTYPE_IMAGE + FTYPE_IMAGE_MOTION) + + cnt->conf.sql_log_snapshot * FTYPE_IMAGE_SNAPSHOT + + cnt->conf.sql_log_movie * (FTYPE_MPEG + FTYPE_MPEG_MOTION) + + cnt->conf.sql_log_timelapse * FTYPE_MPEG_TIMELAPSE; #endif /* defined(HAVE_MYSQL) || defined(HAVE_PGSQL) */ - } + } - /***** MOTION LOOP - FRAMERATE TIMING AND SLEEPING SECTION *****/ + /***** MOTION LOOP - FRAMERATE TIMING AND SLEEPING SECTION *****/ - /* Work out expected frame rate based on config setting which may - have changed from http-control */ - if (cnt->conf.frame_limit) - required_frame_time = 1000000L / cnt->conf.frame_limit; - else - required_frame_time = 0; + /* Work out expected frame rate based on config setting which may + have changed from http-control */ + if (cnt->conf.frame_limit) + required_frame_time = 1000000L / cnt->conf.frame_limit; + else + required_frame_time = 0; - /* Get latest time to calculate time taken to process video data */ - gettimeofday(&tv2, NULL); - elapsedtime = (tv2.tv_usec + 1000000L * tv2.tv_sec) - timenow; + /* Get latest time to calculate time taken to process video data */ + gettimeofday(&tv2, NULL); + elapsedtime = (tv2.tv_usec + 1000000L * tv2.tv_sec) - timenow; - /* Update history buffer but ignore first pass as timebefore - variable will be inaccurate - */ - if (passflag) - rolling_average_data[rolling_frame] = timenow-timebefore; - else - passflag = 1; + /* Update history buffer but ignore first pass as timebefore + variable will be inaccurate + */ + if (passflag) + rolling_average_data[rolling_frame] = timenow-timebefore; + else + passflag = 1; - rolling_frame++; - if (rolling_frame >= rolling_average_limit) - rolling_frame = 0; + rolling_frame++; + if (rolling_frame >= rolling_average_limit) + rolling_frame = 0; - /* Calculate 10 second average and use deviation in delay calculation */ - rolling_average = 0L; + /* Calculate 10 second average and use deviation in delay calculation */ + rolling_average = 0L; - for (j = 0; j < rolling_average_limit; j++) - rolling_average += rolling_average_data[j]; + for (j = 0; j < rolling_average_limit; j++) + rolling_average += rolling_average_data[j]; - rolling_average /= rolling_average_limit; - frame_delay = required_frame_time-elapsedtime - (rolling_average - required_frame_time); + rolling_average /= rolling_average_limit; + frame_delay = required_frame_time-elapsedtime - (rolling_average - required_frame_time); - if (frame_delay > 0) { - /* Apply delay to meet frame time */ - if (frame_delay > required_frame_time) - frame_delay = required_frame_time; + if (frame_delay > 0) { + /* Apply delay to meet frame time */ + if (frame_delay > required_frame_time) + frame_delay = required_frame_time; - /* Delay time in nanoseconds for SLEEP */ - delay_time_nsec = frame_delay * 1000; + /* Delay time in nanoseconds for SLEEP */ + delay_time_nsec = frame_delay * 1000; - if (delay_time_nsec > 999999999) - delay_time_nsec = 999999999; + if (delay_time_nsec > 999999999) + delay_time_nsec = 999999999; - /* SLEEP as defined in motion.h A safe sleep using nanosleep */ - SLEEP(0, delay_time_nsec); - } - } + /* SLEEP as defined in motion.h A safe sleep using nanosleep */ + SLEEP(0, delay_time_nsec); + } + } - /* END OF MOTION MAIN LOOP - * If code continues here it is because the thread is exiting or restarting - */ + /* END OF MOTION MAIN LOOP + * If code continues here it is because the thread is exiting or restarting + */ err: - if (rolling_average_data) - free(rolling_average_data); + if (rolling_average_data) + free(rolling_average_data); - cnt->lost_connection = 1; - motion_log(-1, 0, "%s: Thread exiting", __FUNCTION__); + cnt->lost_connection = 1; + motion_log(-1, 0, "%s: Thread exiting", __FUNCTION__); - motion_cleanup(cnt); + motion_cleanup(cnt); - pthread_mutex_lock(&global_lock); - threads_running--; - pthread_mutex_unlock(&global_lock); + pthread_mutex_lock(&global_lock); + threads_running--; + pthread_mutex_unlock(&global_lock); - if (!cnt->restart) - cnt->watchdog = WATCHDOG_OFF; - cnt->running = 0; - cnt->finish = 0; + if (!cnt->restart) + cnt->watchdog = WATCHDOG_OFF; + cnt->running = 0; + cnt->finish = 0; - pthread_exit(NULL); + pthread_exit(NULL); } /** @@ -1975,85 +2032,85 @@ static void *motion_loop(void *arg) */ static void become_daemon(void) { - int i; - FILE *pidf = NULL; - struct sigaction sig_ign_action; + int i; + FILE *pidf = NULL; + struct sigaction sig_ign_action; - /* Setup sig_ign_action */ + /* Setup sig_ign_action */ #ifdef SA_RESTART - sig_ign_action.sa_flags = SA_RESTART; + sig_ign_action.sa_flags = SA_RESTART; #else - sig_ign_action.sa_flags = 0; + sig_ign_action.sa_flags = 0; #endif - sig_ign_action.sa_handler = SIG_IGN; - sigemptyset(&sig_ign_action.sa_mask); - - /* fork */ - if (fork()) { - motion_log(-1, 0, "%s: Motion going to daemon mode", __FUNCTION__); - exit(0); - } - - /* Create the pid file if defined, if failed exit - * If we fail we report it. If we succeed we postpone the log entry till - * later when we have closed stdout. Otherwise Motion hangs in the terminal waiting - * for an enter. - */ - if (cnt_list[0]->conf.pid_file) { - pidf = fopen(cnt_list[0]->conf.pid_file, "w+"); - - if ( pidf ) { - (void)fprintf(pidf, "%d\n", getpid()); - fclose(pidf); - } else { - motion_log(LOG_ERR, 1, "%s: Exit motion, cannot create process id file (pid file) %s", - __FUNCTION__, cnt_list[0]->conf.pid_file); - exit(0); - } - } - - /* changing dir to root enables people to unmount a disk - without having to stop Motion */ - if (chdir("/")) { - motion_log(LOG_ERR, 1, "%s: Could not change directory", __FUNCTION__); - } + sig_ign_action.sa_handler = SIG_IGN; + sigemptyset(&sig_ign_action.sa_mask); + + /* fork */ + if (fork()) { + motion_log(-1, 0, "%s: Motion going to daemon mode", __FUNCTION__); + exit(0); + } + + /* Create the pid file if defined, if failed exit + * If we fail we report it. If we succeed we postpone the log entry till + * later when we have closed stdout. Otherwise Motion hangs in the terminal waiting + * for an enter. + */ + if (cnt_list[0]->conf.pid_file) { + pidf = myfopen(cnt_list[0]->conf.pid_file, "w+"); + + if (pidf) { + (void)fprintf(pidf, "%d\n", getpid()); + fclose(pidf); + } else { + motion_log(LOG_ERR, 1, "%s: Exit motion, cannot create process id file (pid file) %s", + __FUNCTION__, cnt_list[0]->conf.pid_file); + exit(0); + } + } + + /* changing dir to root enables people to unmount a disk + without having to stop Motion */ + if (chdir("/")) + motion_log(LOG_ERR, 1, "%s: Could not change directory", __FUNCTION__); + #if (defined(BSD)) - setpgrp(0, getpid()); + setpgrp(0, getpid()); #else - setpgrp(); + setpgrp(); #endif /* BSD */ - - if ((i = open("/dev/tty", O_RDWR)) >= 0) { - ioctl(i, TIOCNOTTY, NULL); - close(i); - } - - setsid(); - i = open("/dev/null", O_RDONLY); - - if(i != -1) { - dup2(i, STDIN_FILENO); - close(i); - } - - i = open("/dev/null", O_WRONLY); - - if(i != -1) { - dup2(i, STDOUT_FILENO); - dup2(i, STDERR_FILENO); - close(i); - } - - /* Now it is safe to add the PID creation to the logs */ - if ( pidf ) - motion_log(LOG_INFO, 0, "%s: Created process id file %s. Process ID is %d", - __FUNCTION__, cnt_list[0]->conf.pid_file, getpid()); - - sigaction(SIGTTOU, &sig_ign_action, NULL); - sigaction(SIGTTIN, &sig_ign_action, NULL); - sigaction(SIGTSTP, &sig_ign_action, NULL); + + if ((i = open("/dev/tty", O_RDWR)) >= 0) { + ioctl(i, TIOCNOTTY, NULL); + close(i); + } + + setsid(); + i = open("/dev/null", O_RDONLY); + + if (i != -1) { + dup2(i, STDIN_FILENO); + close(i); + } + + i = open("/dev/null", O_WRONLY); + + if (i != -1) { + dup2(i, STDOUT_FILENO); + dup2(i, STDERR_FILENO); + close(i); + } + + /* Now it is safe to add the PID creation to the logs */ + if (pidf) + motion_log(LOG_INFO, 0, "%s: Created process id file %s. Process ID is %d", + __FUNCTION__, cnt_list[0]->conf.pid_file, getpid()); + + sigaction(SIGTTOU, &sig_ign_action, NULL); + sigaction(SIGTTIN, &sig_ign_action, NULL); + sigaction(SIGTSTP, &sig_ign_action, NULL); } /** @@ -2071,28 +2128,28 @@ static void become_daemon(void) */ static void cntlist_create(int argc, char *argv[]) { - /* cnt_list is an array of pointers to the context structures cnt for each thread. - * First we reserve room for a pointer to thread 0's context structure - * and a NULL pointer which indicates that end of the array of pointers to - * thread context structures. - */ - cnt_list = mymalloc(sizeof(struct context *)*2); - - /* Now we reserve room for thread 0's context structure and let cnt_list[0] point to it */ - cnt_list[0] = mymalloc(sizeof(struct context)); - - /* Populate context structure with start/default values */ - context_init(cnt_list[0]); - - /* cnt_list[1] pointing to zero indicates no more thread context structures - they get added later */ - cnt_list[1] = NULL; - - /* Command line arguments are being pointed to from cnt_list[0] and we call conf_load which loads - * the config options from motion.conf, thread config files and the command line. - */ - cnt_list[0]->conf.argv = argv; - cnt_list[0]->conf.argc = argc; - cnt_list = conf_load(cnt_list); + /* cnt_list is an array of pointers to the context structures cnt for each thread. + * First we reserve room for a pointer to thread 0's context structure + * and a NULL pointer which indicates that end of the array of pointers to + * thread context structures. + */ + cnt_list = mymalloc(sizeof(struct context *) * 2); + + /* Now we reserve room for thread 0's context structure and let cnt_list[0] point to it */ + cnt_list[0] = mymalloc(sizeof(struct context)); + + /* Populate context structure with start/default values */ + context_init(cnt_list[0]); + + /* cnt_list[1] pointing to zero indicates no more thread context structures - they get added later */ + cnt_list[1] = NULL; + + /* Command line arguments are being pointed to from cnt_list[0] and we call conf_load which loads + * the config options from motion.conf, thread config files and the command line. + */ + cnt_list[0]->conf.argv = argv; + cnt_list[0]->conf.argc = argc; + cnt_list = conf_load(cnt_list); } @@ -2109,17 +2166,17 @@ static void cntlist_create(int argc, char *argv[]) */ static void motion_shutdown(void) { - int i = -1; - - motion_remove_pid(); - - while (cnt_list[++i]){ - context_destroy(cnt_list[i]); - } - free(cnt_list); - cnt_list = NULL; + int i = -1; + + motion_remove_pid(); + + while (cnt_list[++i]) + context_destroy(cnt_list[i]); + + free(cnt_list); + cnt_list = NULL; #ifndef WITHOUT_V4L - vid_cleanup(); + vid_cleanup(); #endif } @@ -2140,30 +2197,30 @@ static void motion_shutdown(void) */ static void motion_startup(int daemonize, int argc, char *argv[]) { - /* Initialize our global mutex */ - pthread_mutex_init(&global_lock, NULL); + /* Initialize our global mutex */ + pthread_mutex_init(&global_lock, NULL); - /* Create the list of context structures and load the - * configuration. - */ - cntlist_create(argc, argv); + /* Create the list of context structures and load the + * configuration. + */ + cntlist_create(argc, argv); - motion_log(LOG_INFO, 0, "%s: Motion "VERSION" Started", __FUNCTION__); + motion_log(LOG_INFO, 0, "%s: Motion "VERSION" Started", __FUNCTION__); - initialize_chars(); + initialize_chars(); - if (daemonize) { - /* If daemon mode is requested, and we're not going into setup mode, - * become daemon. - */ - if (cnt_list[0]->daemon && cnt_list[0]->conf.setup_mode == 0) { - become_daemon(); - motion_log(LOG_INFO, 0, "%s: Motion running as daemon process", __FUNCTION__); - } - } + if (daemonize) { + /* If daemon mode is requested, and we're not going into setup mode, + * become daemon. + */ + if (cnt_list[0]->daemon && cnt_list[0]->conf.setup_mode == 0) { + become_daemon(); + motion_log(LOG_INFO, 0, "%s: Motion running as daemon process", __FUNCTION__); + } + } #ifndef WITHOUT_V4L - vid_init(); + vid_init(); #endif } @@ -2179,29 +2236,29 @@ static void motion_startup(int daemonize, int argc, char *argv[]) static void setup_signals(struct sigaction *sig_handler_action, struct sigaction *sigchild_action) { #ifdef SA_NOCLDWAIT - sigchild_action->sa_flags = SA_NOCLDWAIT; + sigchild_action->sa_flags = SA_NOCLDWAIT; #else - sigchild_action->sa_flags = 0; + sigchild_action->sa_flags = 0; #endif - sigchild_action->sa_handler = sigchild_handler; - sigemptyset(&sigchild_action->sa_mask); + sigchild_action->sa_handler = sigchild_handler; + sigemptyset(&sigchild_action->sa_mask); #ifdef SA_RESTART - sig_handler_action->sa_flags = SA_RESTART; + sig_handler_action->sa_flags = SA_RESTART; #else - sig_handler_action->sa_flags = 0; + sig_handler_action->sa_flags = 0; #endif - sig_handler_action->sa_handler = sig_handler; - sigemptyset(&sig_handler_action->sa_mask); - - /* Enable automatic zombie reaping */ - sigaction(SIGCHLD, sigchild_action, NULL); - sigaction(SIGPIPE, sigchild_action, NULL); - sigaction(SIGALRM, sig_handler_action, NULL); - sigaction(SIGHUP, sig_handler_action, NULL); - sigaction(SIGINT, sig_handler_action, NULL); - sigaction(SIGQUIT, sig_handler_action, NULL); - sigaction(SIGTERM, sig_handler_action, NULL); - sigaction(SIGUSR1, sig_handler_action, NULL); + sig_handler_action->sa_handler = sig_handler; + sigemptyset(&sig_handler_action->sa_mask); + + /* Enable automatic zombie reaping */ + sigaction(SIGCHLD, sigchild_action, NULL); + sigaction(SIGPIPE, sigchild_action, NULL); + sigaction(SIGALRM, sig_handler_action, NULL); + sigaction(SIGHUP, sig_handler_action, NULL); + sigaction(SIGINT, sig_handler_action, NULL); + sigaction(SIGQUIT, sig_handler_action, NULL); + sigaction(SIGTERM, sig_handler_action, NULL); + sigaction(SIGUSR1, sig_handler_action, NULL); } /** @@ -2218,60 +2275,60 @@ static void setup_signals(struct sigaction *sig_handler_action, struct sigaction */ static void start_motion_thread(struct context *cnt, pthread_attr_t *thread_attr) { - int i; - - /* Check the stream port number for conflicts. - * First we check for conflict with the control port. - * Second we check for that two threads does not use the same port number - * for the stream. If a duplicate port is found the stream feature gets disabled (port = 0) - * for this thread and a warning is written to console and syslog. - */ - - if (cnt->conf.stream_port != 0) { - /* Compare against the control port. */ - if (cnt_list[0]->conf.webcontrol_port == cnt->conf.stream_port) { - motion_log(LOG_ERR, 0, - "%s: Stream port number %d for thread %d conflicts with the control port", - __FUNCTION__, cnt->conf.stream_port, cnt->threadnr); - motion_log(LOG_ERR, 0, "%s: Stream feature for thread %d is disabled.", - __FUNCTION__, cnt->threadnr); - cnt->conf.stream_port = 0; - } - - /* Compare against stream ports of other threads. */ - for (i = 1; cnt_list[i]; i++) { - if (cnt_list[i] == cnt) - continue; - if (cnt_list[i]->conf.stream_port == cnt->conf.stream_port) { - motion_log(LOG_ERR, 0, - "%s: Stream port number %d for thread %d conflicts with thread %d", - __FUNCTION__, cnt->conf.stream_port, cnt->threadnr, cnt_list[i]->threadnr); - motion_log(LOG_ERR, 0, - "%s: Stream feature for thread %d is disabled.", - __FUNCTION__, cnt->threadnr); - cnt->conf.stream_port = 0; - } - } - } - - /* Update how many threads we have running. This is done within a - * mutex lock to prevent multiple simultaneous updates to - * 'threads_running'. - */ - pthread_mutex_lock(&global_lock); - threads_running++; - pthread_mutex_unlock(&global_lock); - - /* Set a flag that we want this thread running */ - cnt->restart = 1; - - /* Give the thread 30s to start */ - cnt->watchdog = 30; - - /* Create the actual thread. Use 'motion_loop' as the thread - * function. - */ - pthread_create(&cnt->thread_id, thread_attr, &motion_loop, cnt); + int i; + + /* Check the stream port number for conflicts. + * First we check for conflict with the control port. + * Second we check for that two threads does not use the same port number + * for the stream. If a duplicate port is found the stream feature gets disabled (port = 0) + * for this thread and a warning is written to console and syslog. + */ + + if (cnt->conf.stream_port != 0) { + /* Compare against the control port. */ + if (cnt_list[0]->conf.webcontrol_port == cnt->conf.stream_port) { + motion_log(LOG_ERR, 0, + "%s: Stream port number %d for thread %d conflicts with the control port", + __FUNCTION__, cnt->conf.stream_port, cnt->threadnr); + motion_log(LOG_ERR, 0, "%s: Stream feature for thread %d is disabled.", + __FUNCTION__, cnt->threadnr); + cnt->conf.stream_port = 0; + } + + /* Compare against stream ports of other threads. */ + for (i = 1; cnt_list[i]; i++) { + if (cnt_list[i] == cnt) + continue; + if (cnt_list[i]->conf.stream_port == cnt->conf.stream_port) { + motion_log(LOG_ERR, 0, + "%s: Stream port number %d for thread %d conflicts with thread %d", + __FUNCTION__, cnt->conf.stream_port, cnt->threadnr, cnt_list[i]->threadnr); + motion_log(LOG_ERR, 0, + "%s: Stream feature for thread %d is disabled.", + __FUNCTION__, cnt->threadnr); + cnt->conf.stream_port = 0; + } + } + } + + /* Update how many threads we have running. This is done within a + * mutex lock to prevent multiple simultaneous updates to + * 'threads_running'. + */ + pthread_mutex_lock(&global_lock); + threads_running++; + pthread_mutex_unlock(&global_lock); + + /* Set a flag that we want this thread running */ + cnt->restart = 1; + + /* Give the thread WATCHDOG_TMO to start */ + cnt->watchdog = WATCHDOG_TMO; + + /* Create the actual thread. Use 'motion_loop' as the thread + * function. + */ + pthread_create(&cnt->thread_id, thread_attr, &motion_loop, cnt); } /** @@ -2289,172 +2346,177 @@ static void start_motion_thread(struct context *cnt, pthread_attr_t *thread_attr */ int main (int argc, char **argv) { - int i; - pthread_attr_t thread_attr; - pthread_t thread_id; + int i; + pthread_attr_t thread_attr; + pthread_t thread_id; - /* Setup signals and do some initialization. 1 in the call to - * 'motion_startup' means that Motion will become a daemon if so has been - * requested, and argc and argc are necessary for reading the command - * line options. - */ - struct sigaction sig_handler_action; - struct sigaction sigchild_action; - setup_signals(&sig_handler_action, &sigchild_action); + /* Setup signals and do some initialization. 1 in the call to + * 'motion_startup' means that Motion will become a daemon if so has been + * requested, and argc and argc are necessary for reading the command + * line options. + */ + struct sigaction sig_handler_action; + struct sigaction sigchild_action; + setup_signals(&sig_handler_action, &sigchild_action); - motion_startup(1, argc, argv); + motion_startup(1, argc, argv); #ifdef HAVE_FFMPEG - /* FFMpeg initialization is only performed if FFMpeg support was found - * and not disabled during the configure phase. - */ - ffmpeg_init(); + /* FFMpeg initialization is only performed if FFMpeg support was found + * and not disabled during the configure phase. + */ + ffmpeg_init(); #endif /* HAVE_FFMPEG */ - /* In setup mode, Motion is very communicative towards the user, which - * allows the user to experiment with the config parameters in order to - * optimize motion detection and stuff. - */ - if(cnt_list[0]->conf.setup_mode) - motion_log(-1, 0, "%s: Motion running in setup mode.", __FUNCTION__); - - /* Create and a thread attribute for the threads we spawn later on. - * PTHREAD_CREATE_DETACHED means to create threads detached, i.e. - * their termination cannot be synchronized through 'pthread_join'. - */ - pthread_attr_init(&thread_attr); - pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); - - /* Create the TLS key for thread number. */ - pthread_key_create(&tls_key_threadnr, NULL); - - do { - if (restart) { - /* Handle the restart situation. Currently the approach is to - * cleanup everything, and then initialize everything again - * (including re-reading the config file(s)). - */ - motion_shutdown(); - restart = 0; /* only one reset for now */ - motion_log(LOG_INFO, 0, "%s: motion restarted", __FUNCTION__); + /* In setup mode, Motion is very communicative towards the user, which + * allows the user to experiment with the config parameters in order to + * optimize motion detection and stuff. + */ + if (cnt_list[0]->conf.setup_mode) + motion_log(-1, 0, "%s: Motion running in setup mode.", __FUNCTION__); + + /* Create and a thread attribute for the threads we spawn later on. + * PTHREAD_CREATE_DETACHED means to create threads detached, i.e. + * their termination cannot be synchronized through 'pthread_join'. + */ + pthread_attr_init(&thread_attr); + pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); + + /* Create the TLS key for thread number. */ + pthread_key_create(&tls_key_threadnr, NULL); + + do { + if (restart) { + /* Handle the restart situation. Currently the approach is to + * cleanup everything, and then initialize everything again + * (including re-reading the config file(s)). + */ + motion_shutdown(); + restart = 0; /* only one reset for now */ + motion_log(LOG_INFO, 0, "%s: motion restarted", __FUNCTION__); #ifndef WITHOUT_V4L - SLEEP(5, 0); // maybe some cameras needs less time + SLEEP(5, 0); // maybe some cameras needs less time #endif - motion_startup(0, argc, argv); /* 0 = skip daemon init */ - } - - - /* Start the motion threads. First 'cnt_list' item is global if 'thread' - * option is used, so start at 1 then and 0 otherwise. - */ - for (i = cnt_list[1] != NULL ? 1 : 0; cnt_list[i]; i++) { - /* If i is 0 it means no thread files and we then set the thread number to 1 */ - cnt_list[i]->threadnr = i ? i : 1; - - if ( strcmp(cnt_list[i]->conf_filename, "") ) - motion_log(LOG_INFO, 0, "%s: Thread %d is from %s", - __FUNCTION__, cnt_list[i]->threadnr, cnt_list[i]->conf_filename ); - - if (debug_level >= CAMERA_DEBUG) { - motion_log(-1, 0, "%s: Thread %d is device: %s input %d", __FUNCTION__, - cnt_list[i]->threadnr, cnt_list[i]->conf.netcam_url ? - cnt_list[i]->conf.netcam_url : cnt_list[i]->conf.video_device, - cnt_list[i]->conf.netcam_url ? -1 : cnt_list[i]->conf.input); - } - - if (debug_level >= CAMERA_DEBUG) - motion_log(LOG_ERR, 0, "%s: Stream port %d", - __FUNCTION__, cnt_list[i]->conf.stream_port); - - start_motion_thread(cnt_list[i], &thread_attr); - } - - /* Create a thread for the control interface if requested. Create it - * detached and with 'motion_web_control' as the thread function. - */ - if (cnt_list[0]->conf.webcontrol_port) - pthread_create(&thread_id, &thread_attr, &motion_web_control, cnt_list); - - if (debug_level >= CAMERA_DEBUG) - motion_log(-1, 0, "%s: Waiting for threads to finish, pid: %d", __FUNCTION__, getpid()); - - /* Crude way of waiting for all threads to finish - check the thread - * counter (because we cannot do join on the detached threads). - */ - while (1) { - SLEEP(1, 0); - - /* Calculate how many threads runnig or wants to run - * if zero and we want to finish, break out - */ - int motion_threads_running = 0; - for (i = (cnt_list[1] != NULL ? 1 : 0); cnt_list[i]; i++) { - if (cnt_list[i]->running || cnt_list[i]->restart) - motion_threads_running++; - } - if ( ((motion_threads_running == 0 ) && finish ) || - ((motion_threads_running == 0 ) && (threads_running == 0)) ){ - if (debug_level >= CAMERA_VERBOSE) - motion_log(LOG_INFO, 0, "%s: DEBUG-1 threads_running %d motion_threads_running %d , finish %d", - __FUNCTION__, threads_running, motion_threads_running, finish); - break; - } - - for (i = (cnt_list[1] != NULL ? 1 : 0); cnt_list[i]; i++) { - /* Check if threads wants to be restarted */ - if ( (!cnt_list[i]->running) && (cnt_list[i]->restart) ) { - motion_log(LOG_INFO, 0, "%s: Motion thread %d restart", - __FUNCTION__, cnt_list[i]->threadnr); - start_motion_thread(cnt_list[i], &thread_attr); - } - if (cnt_list[i]->watchdog > WATCHDOG_OFF) { - cnt_list[i]->watchdog--; - if (cnt_list[i]->watchdog == 0) { - motion_log(LOG_ERR, 0, "%s: Thread %d - Watchdog timeout, trying to do " - "a graceful restart", __FUNCTION__, cnt_list[i]->threadnr); - cnt_list[i]->finish = 1; - } - if (cnt_list[i]->watchdog == -60) { - motion_log(LOG_ERR, 0, "%s: Thread %d - Watchdog timeout, did NOT restart graceful," - "killing it!", __FUNCTION__, cnt_list[i]->threadnr); - pthread_cancel(cnt_list[i]->thread_id); - pthread_mutex_lock(&global_lock); - threads_running--; - pthread_mutex_unlock(&global_lock); - motion_cleanup(cnt_list[i]); - cnt_list[i]->running = 0; - cnt_list[i]->finish = 0; - } - } - } - if (debug_level >= CAMERA_VERBOSE) - motion_log(LOG_INFO, 0, "%s: DEBUG-2 threads_running %d motion_threads_running %d finish %d", - __FUNCTION__, threads_running, motion_threads_running, finish); - } - /* Reset end main loop flag */ - finish = 0; - - if (debug_level >= CAMERA_DEBUG) - motion_log(LOG_DEBUG, 0, "%s: Threads finished", __FUNCTION__); - - /* Rest for a while if we're supposed to restart. */ - if (restart) - SLEEP(2, 0); - - } while (restart); /* loop if we're supposed to restart */ - - // Be sure that http control exits fine - cnt_list[0]->finish = 1; - SLEEP(1, 0); - motion_log(LOG_INFO, 0, "%s: Motion terminating", __FUNCTION__); - - /* Perform final cleanup. */ - pthread_key_delete(tls_key_threadnr); - pthread_attr_destroy(&thread_attr); - pthread_mutex_destroy(&global_lock); - motion_shutdown(); - - return 0; + motion_startup(0, argc, argv); /* 0 = skip daemon init */ + } + + + /* Start the motion threads. First 'cnt_list' item is global if 'thread' + * option is used, so start at 1 then and 0 otherwise. + */ + for (i = cnt_list[1] != NULL ? 1 : 0; cnt_list[i]; i++) { + /* If i is 0 it means no thread files and we then set the thread number to 1 */ + cnt_list[i]->threadnr = i ? i : 1; + + if (strcmp(cnt_list[i]->conf_filename, "")) + motion_log(LOG_INFO, 0, "%s: Thread %d is from %s", + __FUNCTION__, cnt_list[i]->threadnr, cnt_list[i]->conf_filename); + + if (debug_level >= CAMERA_DEBUG) + motion_log(-1, 0, "%s: Thread %d is device: %s input %d", __FUNCTION__, + cnt_list[i]->threadnr, cnt_list[i]->conf.netcam_url ? + cnt_list[i]->conf.netcam_url : cnt_list[i]->conf.video_device, + cnt_list[i]->conf.netcam_url ? -1 : cnt_list[i]->conf.input); + + + if (debug_level >= CAMERA_DEBUG) + motion_log(LOG_ERR, 0, "%s: Stream port %d", + __FUNCTION__, cnt_list[i]->conf.stream_port); + + start_motion_thread(cnt_list[i], &thread_attr); + } + + /* Create a thread for the control interface if requested. Create it + * detached and with 'motion_web_control' as the thread function. + */ + if (cnt_list[0]->conf.webcontrol_port) + pthread_create(&thread_id, &thread_attr, &motion_web_control, cnt_list); + + if (debug_level >= CAMERA_DEBUG) + motion_log(-1, 0, "%s: Waiting for threads to finish, pid: %d", __FUNCTION__, getpid()); + + /* Crude way of waiting for all threads to finish - check the thread + * counter (because we cannot do join on the detached threads). + */ + while (1) { + SLEEP(1, 0); + + /* Calculate how many threads runnig or wants to run + * if zero and we want to finish, break out + */ + int motion_threads_running = 0; + + for (i = (cnt_list[1] != NULL ? 1 : 0); cnt_list[i]; i++) { + if (cnt_list[i]->running || cnt_list[i]->restart) + motion_threads_running++; + } + + if (((motion_threads_running == 0) && finish) || + ((motion_threads_running == 0) && (threads_running == 0))) { + if (debug_level >= CAMERA_VERBOSE) + motion_log(LOG_INFO, 0, "%s: DEBUG-1 threads_running %d motion_threads_running %d " + ", finish %d", __FUNCTION__, threads_running, motion_threads_running, finish); + break; + } + + for (i = (cnt_list[1] != NULL ? 1 : 0); cnt_list[i]; i++) { + /* Check if threads wants to be restarted */ + if ((!cnt_list[i]->running) && (cnt_list[i]->restart)) { + motion_log(LOG_INFO, 0, "%s: Motion thread %d restart", + __FUNCTION__, cnt_list[i]->threadnr); + start_motion_thread(cnt_list[i], &thread_attr); + } + + if (cnt_list[i]->watchdog > WATCHDOG_OFF) { + cnt_list[i]->watchdog--; + + if (cnt_list[i]->watchdog == 0) { + motion_log(LOG_ERR, 0, "%s: Thread %d - Watchdog timeout, trying to do " + "a graceful restart", __FUNCTION__, cnt_list[i]->threadnr); + cnt_list[i]->finish = 1; + } + + if (cnt_list[i]->watchdog == -60) { + motion_log(LOG_ERR, 0, "%s: Thread %d - Watchdog timeout, did NOT restart graceful," + "killing it!", __FUNCTION__, cnt_list[i]->threadnr); + pthread_cancel(cnt_list[i]->thread_id); + pthread_mutex_lock(&global_lock); + threads_running--; + pthread_mutex_unlock(&global_lock); + motion_cleanup(cnt_list[i]); + cnt_list[i]->running = 0; + cnt_list[i]->finish = 0; + } + } + } + if (debug_level >= CAMERA_VERBOSE) + motion_log(LOG_INFO, 0, "%s: DEBUG-2 threads_running %d motion_threads_running %d finish %d", + __FUNCTION__, threads_running, motion_threads_running, finish); + } + /* Reset end main loop flag */ + finish = 0; + + if (debug_level >= CAMERA_DEBUG) + motion_log(LOG_DEBUG, 0, "%s: Threads finished", __FUNCTION__); + + /* Rest for a while if we're supposed to restart. */ + if (restart) + SLEEP(2, 0); + + } while (restart); /* loop if we're supposed to restart */ + + // Be sure that http control exits fine + cnt_list[0]->finish = 1; + SLEEP(1, 0); + motion_log(LOG_INFO, 0, "%s: Motion terminating", __FUNCTION__); + + /* Perform final cleanup. */ + pthread_key_delete(tls_key_threadnr); + pthread_attr_destroy(&thread_attr); + pthread_mutex_destroy(&global_lock); + motion_shutdown(); + + return 0; } /** @@ -2478,15 +2540,16 @@ int main (int argc, char **argv) */ void * mymalloc(size_t nbytes) { - void *dummy = malloc(nbytes); - if (!dummy) { - motion_log(LOG_EMERG, 1, "%s: Could not allocate %llu bytes of memory!", - __FUNCTION__, (unsigned long long)nbytes); - motion_remove_pid(); - exit(1); - } - - return dummy; + void *dummy = malloc(nbytes); + + if (!dummy) { + motion_log(LOG_EMERG, 1, "%s: Could not allocate %llu bytes of memory!", + __FUNCTION__, (unsigned long long)nbytes); + motion_remove_pid(); + exit(1); + } + + return dummy; } /** @@ -2507,25 +2570,25 @@ void * mymalloc(size_t nbytes) */ void *myrealloc(void *ptr, size_t size, const char *desc) { - void *dummy = NULL; - - if (size == 0) { - free(ptr); - motion_log(LOG_WARNING, 0, - "%s: Warning! Function %s tries to resize memoryblock at %p to 0 bytes!", - __FUNCTION__, desc, ptr); - } else { - dummy = realloc(ptr, size); - if (!dummy) { - motion_log(LOG_EMERG, 0, - "%s: Could not resize memory-block at offset %p to %llu bytes (function %s)!", - __FUNCTION__, ptr, (unsigned long long)size, desc); - motion_remove_pid(); - exit(1); - } - } - - return dummy; + void *dummy = NULL; + + if (size == 0) { + free(ptr); + motion_log(LOG_WARNING, 0, + "%s: Warning! Function %s tries to resize memoryblock at %p to 0 bytes!", + __FUNCTION__, desc, ptr); + } else { + dummy = realloc(ptr, size); + if (!dummy) { + motion_log(LOG_EMERG, 0, + "%s: Could not resize memory-block at offset %p to %llu bytes (function %s)!", + __FUNCTION__, ptr, (unsigned long long)size, desc); + motion_remove_pid(); + exit(1); + } + } + + return dummy; } @@ -2546,31 +2609,31 @@ void *myrealloc(void *ptr, size_t size, const char *desc) */ int create_path(const char *path) { - char *start; - mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + char *start; + mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; - if (path[0] == '/') - start = strchr(path + 1, '/'); - else - start = strchr(path, '/'); + if (path[0] == '/') + start = strchr(path + 1, '/'); + else + start = strchr(path, '/'); - while(start) { - char *buffer = mystrdup(path); - buffer[start-path] = 0x00; + while(start) { + char *buffer = mystrdup(path); + buffer[start-path] = 0x00; - if (mkdir(buffer, mode) == -1 && errno != EEXIST) { - motion_log(LOG_ERR, 1, "%s: Problem creating directory %s", - __FUNCTION__, buffer); - free(buffer); - return -1; - } + if (mkdir(buffer, mode) == -1 && errno != EEXIST) { + motion_log(LOG_ERR, 1, "%s: Problem creating directory %s", + __FUNCTION__, buffer); + free(buffer); + return -1; + } - free(buffer); + free(buffer); - start = strchr(start + 1, '/'); - } + start = strchr(start + 1, '/'); + } - return 0; + return 0; } /** @@ -2591,36 +2654,35 @@ int create_path(const char *path) */ FILE * myfopen(const char *path, const char *mode) { - /* first, just try to open the file */ - FILE *dummy = fopen(path, mode); - - /* could not open file... */ - if (!dummy) { - /* path did not exist? */ - if (errno == ENOENT) { - //DEBUG CODE syslog(LOG_DEBUG, "Could not open file %s directly; path did not exist. Creating path & retrying.", path); - - /* create path for file... */ - if (create_path(path) == -1) - return NULL; - - /* and retry opening the file */ - dummy = fopen(path, mode); - if (dummy) - return dummy; - } - - /* two possibilities - * 1: there was an other error while trying to open the file for the first time - * 2: could still not open the file after the path was created - */ - motion_log(LOG_ERR, 1, "%s: Error opening file %s with mode %s", - __FUNCTION__, path, mode); - - return NULL; - } - - return dummy; + /* first, just try to open the file */ + FILE *dummy = fopen(path, mode); + + /* could not open file... */ + if (!dummy) { + /* path did not exist? */ + if (errno == ENOENT) { + + /* create path for file... */ + if (create_path(path) == -1) + return NULL; + + /* and retry opening the file */ + dummy = fopen(path, mode); + if (dummy) + return dummy; + } + + /* two possibilities + * 1: there was an other error while trying to open the file for the first time + * 2: could still not open the file after the path was created + */ + motion_log(LOG_ERR, 1, "%s: Error opening file %s with mode %s", + __FUNCTION__, path, mode); + + return NULL; + } + + return dummy; } /** @@ -2645,119 +2707,123 @@ FILE * myfopen(const char *path, const char *mode) size_t mystrftime(struct context *cnt, char *s, size_t max, const char *userformat, const struct tm *tm, const char *filename, int sqltype) { - char formatstring[PATH_MAX] = ""; - char tempstring[PATH_MAX] = ""; - char *format, *tempstr; - const char *pos_userformat; - - format = formatstring; - - /* if mystrftime is called with userformat = NULL we return a zero length string */ - if (userformat == NULL) { - *s = '\0'; - return 0; - } - - for (pos_userformat = userformat; *pos_userformat; ++pos_userformat) { - - if (*pos_userformat == '%') { - /* Reset 'tempstr' to point to the beginning of 'tempstring', - * otherwise we will eat up tempstring if there are many - * format specifiers. - */ - tempstr = tempstring; - tempstr[0] = '\0'; - switch (*++pos_userformat) { - case '\0': // end of string - --pos_userformat; - break; - - case 'v': // event - sprintf(tempstr, "%02d", cnt->event_nr); - break; - - case 'q': // shots - sprintf(tempstr, "%02d", cnt->current_image->shot); - break; - - case 'D': // diffs - sprintf(tempstr, "%d", cnt->current_image->diffs); - break; - - case 'N': // noise - sprintf(tempstr, "%d", cnt->noise); - break; - - case 'i': // motion width - sprintf(tempstr, "%d", cnt->current_image->location.width); - break; - - case 'J': // motion height - sprintf(tempstr, "%d", cnt->current_image->location.height); - break; - - case 'K': // motion center x - sprintf(tempstr, "%d", cnt->current_image->location.x); - break; - - case 'L': // motion center y - sprintf(tempstr, "%d", cnt->current_image->location.y); - break; - - case 'o': // threshold - sprintf(tempstr, "%d", cnt->threshold); - break; - - case 'Q': // number of labels - sprintf(tempstr, "%d", cnt->current_image->total_labels); - break; - case 't': // thread number - sprintf(tempstr, "%d",(int)(unsigned long) - pthread_getspecific(tls_key_threadnr)); - break; - case 'C': // text_event - if (cnt->text_event_string && cnt->text_event_string[0]) - snprintf(tempstr, PATH_MAX, "%s", cnt->text_event_string); - else - ++pos_userformat; - break; - case 'f': // filename - if (filename) - snprintf(tempstr, PATH_MAX, "%s", filename); - else - ++pos_userformat; - break; - case 'n': // sqltype - if (sqltype) - sprintf(tempstr, "%d", sqltype); - else - ++pos_userformat; - break; - default: // Any other code is copied with the %-sign - *format++ = '%'; - *format++ = *pos_userformat; - continue; - } - - /* If a format specifier was found and used, copy the result from - * 'tempstr' to 'format'. - */ - if (tempstr[0]) { - while ((*format = *tempstr++) != '\0') - ++format; - continue; - } - } - - /* For any other character than % we just simply copy the character */ - *format++ = *pos_userformat; - } - - *format = '\0'; - format = formatstring; - - return strftime(s, max, format, tm); + char formatstring[PATH_MAX] = ""; + char tempstring[PATH_MAX] = ""; + char *format, *tempstr; + const char *pos_userformat; + + format = formatstring; + + /* if mystrftime is called with userformat = NULL we return a zero length string */ + if (userformat == NULL) { + *s = '\0'; + return 0; + } + + for (pos_userformat = userformat; *pos_userformat; ++pos_userformat) { + + if (*pos_userformat == '%') { + /* Reset 'tempstr' to point to the beginning of 'tempstring', + * otherwise we will eat up tempstring if there are many + * format specifiers. + */ + tempstr = tempstring; + tempstr[0] = '\0'; + + switch (*++pos_userformat) { + case '\0': // end of string + --pos_userformat; + break; + + case 'v': // event + sprintf(tempstr, "%02d", cnt->event_nr); + break; + + case 'q': // shots + sprintf(tempstr, "%02d", cnt->current_image->shot); + break; + + case 'D': // diffs + sprintf(tempstr, "%d", cnt->current_image->diffs); + break; + + case 'N': // noise + sprintf(tempstr, "%d", cnt->noise); + break; + + case 'i': // motion width + sprintf(tempstr, "%d", cnt->current_image->location.width); + break; + + case 'J': // motion height + sprintf(tempstr, "%d", cnt->current_image->location.height); + break; + + case 'K': // motion center x + sprintf(tempstr, "%d", cnt->current_image->location.x); + break; + + case 'L': // motion center y + sprintf(tempstr, "%d", cnt->current_image->location.y); + break; + + case 'o': // threshold + sprintf(tempstr, "%d", cnt->threshold); + break; + + case 'Q': // number of labels + sprintf(tempstr, "%d", cnt->current_image->total_labels); + break; + + case 't': // thread number + sprintf(tempstr, "%d",(int)(unsigned long) + pthread_getspecific(tls_key_threadnr)); + break; + + case 'C': // text_event + if (cnt->text_event_string && cnt->text_event_string[0]) + snprintf(tempstr, PATH_MAX, "%s", cnt->text_event_string); + else + ++pos_userformat; + break; + + case 'f': // filename + if (filename) + snprintf(tempstr, PATH_MAX, "%s", filename); + else + ++pos_userformat; + break; + + case 'n': // sqltype + if (sqltype) + sprintf(tempstr, "%d", sqltype); + else + ++pos_userformat; + break; + + default: // Any other code is copied with the %-sign + *format++ = '%'; + *format++ = *pos_userformat; + continue; + } + + /* If a format specifier was found and used, copy the result from + * 'tempstr' to 'format'. + */ + if (tempstr[0]) { + while ((*format = *tempstr++) != '\0') + ++format; + continue; + } + } + + /* For any other character than % we just simply copy the character */ + *format++ = *pos_userformat; + } + + *format = '\0'; + format = formatstring; + + return strftime(s, max, format, tm); } - - diff --git a/netcam.c b/netcam.c index 94259b6..c811ea0 100644 --- a/netcam.c +++ b/netcam.c @@ -41,16 +41,16 @@ #include #include -#include /* For parsing of the URL */ +#include /* For parsing of the URL */ #include #include "netcam_ftp.h" -#define CONNECT_TIMEOUT 10 /* timeout on remote connection attempt */ -#define READ_TIMEOUT 5 /* default timeout on recv requests */ +#define CONNECT_TIMEOUT 10 /* timeout on remote connection attempt */ +#define READ_TIMEOUT 5 /* default timeout on recv requests */ #define POLLING_TIMEOUT READ_TIMEOUT /* file polling timeout [s] */ -#define POLLING_TIME 500*1000*1000 /* file polling time quantum [ns] (500ms) */ -#define MAX_HEADER_RETRIES 5 /* Max tries to find a header record */ +#define POLLING_TIME 500*1000*1000 /* file polling time quantum [ns] (500ms) */ +#define MAX_HEADER_RETRIES 5 /* Max tries to find a header record */ #define MINVAL(x, y) ((x) < (y) ? (x) : (y)) /* @@ -66,21 +66,21 @@ tfile_context *file_new_context(void); void file_free_context(tfile_context* ctxt); /* These strings are used for the HTTP connection */ -static const char *connect_req; +static const char *connect_req; -static const char *connect_req_http10 = "GET %s HTTP/1.0\r\n" - "Host: %s\r\n" - "User-Agent: Motion-netcam/" VERSION "\r\n"; +static const char *connect_req_http10 = "GET %s HTTP/1.0\r\n" + "Host: %s\r\n" + "User-Agent: Motion-netcam/" VERSION "\r\n"; -static const char *connect_req_http11 = "GET %s HTTP/1.1\r\n" - "Host: %s\r\n" - "User-Agent: Motion-netcam/" VERSION "\r\n"; +static const char *connect_req_http11 = "GET %s HTTP/1.1\r\n" + "Host: %s\r\n" + "User-Agent: Motion-netcam/" VERSION "\r\n"; -static const char *connect_req_close = "Connection: close\r\n"; +static const char *connect_req_close = "Connection: close\r\n"; -static const char *connect_req_keepalive = "Connection: Keep-Alive\r\n"; +static const char *connect_req_keepalive = "Connection: Keep-Alive\r\n"; -static const char *connect_auth_req = "Authorization: Basic %s\r\n"; +static const char *connect_auth_req = "Authorization: Basic %s\r\n"; /* * The following three routines (netcam_url_match, netcam_url_parse and @@ -123,19 +123,19 @@ static const char *connect_auth_req = "Authorization: Basic %s\r\n"; */ static char *netcam_url_match(regmatch_t m, const char *input) { - char *match = NULL; - int len; + char *match = NULL; + int len; - if (m.rm_so != -1) { - len = m.rm_eo - m.rm_so; + if (m.rm_so != -1) { + len = m.rm_eo - m.rm_so; - if ((match = (char *) mymalloc(len + 1)) != NULL) { - strncpy(match, input + m.rm_so, len); - match[len] = '\0'; - } - } + if ((match = (char *) mymalloc(len + 1)) != NULL) { + strncpy(match, input + m.rm_so, len); + match[len] = '\0'; + } + } - return (match); + return (match); } /** @@ -153,68 +153,68 @@ static char *netcam_url_match(regmatch_t m, const char *input) */ static void netcam_url_parse(struct url_t *parse_url, const char *text_url) { - char *s; - int i; - const char *re = "(http|ftp|mjpg)://(((.*):(.*))@)?" - "([^/:]|[-.a-z0-9]+)(:([0-9]+))?($|(/[^:]*))"; - regex_t pattbuf; - regmatch_t matches[10]; - - if( !strncmp( text_url, "file", 4 ) ) - re = "(file)://(((.*):(.*))@)?" - "([^/:]|[-.a-z0-9]*)(:([0-9]*))?($|(/[^:][/-_.a-z0-9]+))"; - - if (debug_level > CAMERA_DEBUG) - motion_log(-1, 0, "%s: Entry netcam_url_parse data %s", __FUNCTION__, text_url); - - memset(parse_url, 0, sizeof(struct url_t)); - /* - * regcomp compiles regular expressions into a form that is - * suitable for regexec searches - * regexec matches the URL string against the regular expression - * and returns an array of pointers to strings matching each match - * within (). The results that we need are finally placed in parse_url - */ - if (!regcomp(&pattbuf, re, REG_EXTENDED | REG_ICASE)) { - if (regexec(&pattbuf, text_url, 10, matches, 0) != REG_NOMATCH) { - for (i = 0; i < 10; i++) { - if ((s = netcam_url_match(matches[i], text_url)) != NULL) { - if (debug_level > CAMERA_DEBUG) - motion_log(-1, 0, "%s: Parse case %d data %s", __FUNCTION__, i, s ); - switch (i) { - case 1: - parse_url->service = s; - break; - case 3: - parse_url->userpass = s; - break; - case 6: - parse_url->host = s; - break; - case 8: - parse_url->port = atoi(s); - free(s); - break; - case 9: - parse_url->path = s; - break; - /* other components ignored */ - default: - free(s); - break; - } - } - } - } - } - if ((!parse_url->port) && (parse_url->service)) { - if (!strcmp(parse_url->service, "http")) - parse_url->port = 80; - else if (!strcmp(parse_url->service, "ftp")) - parse_url->port = 21; - } - - regfree(&pattbuf); + char *s; + int i; + const char *re = "(http|ftp|mjpg)://(((.*):(.*))@)?" + "([^/:]|[-.a-z0-9]+)(:([0-9]+))?($|(/[^:]*))"; + regex_t pattbuf; + regmatch_t matches[10]; + + if (!strncmp(text_url, "file", 4)) + re = "(file)://(((.*):(.*))@)?" + "([^/:]|[-.a-z0-9]*)(:([0-9]*))?($|(/[^:][/-_.a-z0-9]+))"; + + if (debug_level > CAMERA_DEBUG) + motion_log(-1, 0, "%s: Entry netcam_url_parse data %s", __FUNCTION__, text_url); + + memset(parse_url, 0, sizeof(struct url_t)); + /* + * regcomp compiles regular expressions into a form that is + * suitable for regexec searches + * regexec matches the URL string against the regular expression + * and returns an array of pointers to strings matching each match + * within (). The results that we need are finally placed in parse_url + */ + if (!regcomp(&pattbuf, re, REG_EXTENDED | REG_ICASE)) { + if (regexec(&pattbuf, text_url, 10, matches, 0) != REG_NOMATCH) { + for (i = 0; i < 10; i++) { + if ((s = netcam_url_match(matches[i], text_url)) != NULL) { + if (debug_level > CAMERA_DEBUG) + motion_log(-1, 0, "%s: Parse case %d data %s", __FUNCTION__, i, s); + switch (i) { + case 1: + parse_url->service = s; + break; + case 3: + parse_url->userpass = s; + break; + case 6: + parse_url->host = s; + break; + case 8: + parse_url->port = atoi(s); + free(s); + break; + case 9: + parse_url->path = s; + break; + /* other components ignored */ + default: + free(s); + break; + } + } + } + } + } + if ((!parse_url->port) && (parse_url->service)) { + if (!strcmp(parse_url->service, "http")) + parse_url->port = 80; + else if (!strcmp(parse_url->service, "ftp")) + parse_url->port = 21; + } + + regfree(&pattbuf); } /** @@ -231,25 +231,25 @@ static void netcam_url_parse(struct url_t *parse_url, const char *text_url) */ static void netcam_url_free(struct url_t *parse_url) { - if (parse_url->service) { - free(parse_url->service); - parse_url->service = NULL; - } - - if (parse_url->userpass) { - free(parse_url->userpass); - parse_url->userpass = NULL; - } - - if (parse_url->host) { - free(parse_url->host); - parse_url->host = NULL; - } - - if (parse_url->path) { - free(parse_url->path); - parse_url->path = NULL; - } + if (parse_url->service) { + free(parse_url->service); + parse_url->service = NULL; + } + + if (parse_url->userpass) { + free(parse_url->userpass); + parse_url->userpass = NULL; + } + + if (parse_url->host) { + free(parse_url->host); + parse_url->host = NULL; + } + + if (parse_url->path) { + free(parse_url->path); + parse_url->path = NULL; + } } /** @@ -267,24 +267,24 @@ static void netcam_url_free(struct url_t *parse_url) */ static void check_quote(char *str) { - int len; - char ch; - - ch = *str; - - if ((ch == '"') || (ch == '\'')) { - len = strlen(str) - 1; - if (str[len] == ch) { - memmove(str, str+1, len-1); - str[len-1] = 0; - } - } + int len; + char ch; + + ch = *str; + + if ((ch == '"') || (ch == '\'')) { + len = strlen(str) - 1; + if (str[len] == ch) { + memmove(str, str+1, len-1); + str[len-1] = 0; + } + } } /** * netcam_check_content_length * - * Analyse an HTTP-header line to see if it is a Content-length + * Analyse an HTTP-header line to see if it is a Content-length * * Parameters: * @@ -297,26 +297,26 @@ static void check_quote(char *str) */ static long netcam_check_content_length(char *header) { - long length = -1; /* note this is a long, not an int */ - - if (!header_process(header, "Content-Length", header_extract_number, &length)) { - /* - * Some netcams deliver some bad-format data, but if - * we were able to recognize the header section and the - * number we might as well try to use it. - */ - if (length > 0) - return length; - return -1; - } - - return length; + long length = -1; /* note this is a long, not an int */ + + if (!header_process(header, "Content-Length", header_extract_number, &length)) { + /* + * Some netcams deliver some bad-format data, but if + * we were able to recognize the header section and the + * number we might as well try to use it. + */ + if (length > 0) + return length; + return -1; + } + + return length; } /** * netcam_check_keepalive * - * Analyse an HTTP-header line to see if it is a Keep-Alive. + * Analyse an HTTP-header line to see if it is a Keep-Alive. * * Parameters: * @@ -329,23 +329,23 @@ static long netcam_check_content_length(char *header) */ static int netcam_check_keepalive(char *header) { - char *content_type = NULL; + char *content_type = NULL; - if (!header_process(header, "Keep-Alive", http_process_type, &content_type)) - return -1; + if (!header_process(header, "Keep-Alive", http_process_type, &content_type)) + return -1; - /* We do not detect the second field or other case mixes at present. */ + /* We do not detect the second field or other case mixes at present. */ - if (content_type) - free(content_type); + if (content_type) + free(content_type); - return 1; + return 1; } /** * netcam_check_close * - * Analyse an HTTP-header line to see if it is a Connection: close + * Analyse an HTTP-header line to see if it is a Connection: close * * Parameters: * @@ -358,25 +358,25 @@ static int netcam_check_keepalive(char *header) */ static int netcam_check_close(char *header) { - char *type = NULL; - int ret = -1; - - if (!header_process(header, "Connection", http_process_type, &type)) - return -1; - - if (!strcmp(type, "close")) /* strcmp returns 0 for match */ - ret = 1; - - if (type) - free(type); - - return ret; + char *type = NULL; + int ret = -1; + + if (!header_process(header, "Connection", http_process_type, &type)) + return -1; + + if (!strcmp(type, "close")) /* strcmp returns 0 for match */ + ret = 1; + + if (type) + free(type); + + return ret; } /** * netcam_check_content_type * - * Analyse an HTTP-header line to see if it is a Content-type + * Analyse an HTTP-header line to see if it is a Content-type * * Parameters: * @@ -392,26 +392,27 @@ static int netcam_check_close(char *header) */ static int netcam_check_content_type(char *header) { - char *content_type = NULL; - int ret; - - if (!header_process(header, "Content-type", http_process_type, &content_type)) - return -1; - - if (!strcmp(content_type, "image/jpeg")) { - ret = 1; - } else if (!strcmp(content_type, "multipart/x-mixed-replace") || - !strcmp(content_type, "multipart/mixed")) { - ret = 2; - } else if (!strcmp(content_type, "application/octet-stream")) { - ret = 3; - } else - ret = 0; - - if (content_type) - free(content_type); - - return ret; + char *content_type = NULL; + int ret; + + if (!header_process(header, "Content-type", http_process_type, &content_type)) + return -1; + + if (!strcmp(content_type, "image/jpeg")) { + ret = 1; + } else if (!strcmp(content_type, "multipart/x-mixed-replace") || + !strcmp(content_type, "multipart/mixed")) { + ret = 2; + } else if (!strcmp(content_type, "application/octet-stream")) { + ret = 3; + } else { + ret = 0; + } + + if (content_type) + free(content_type); + + return ret; } @@ -429,89 +430,90 @@ static int netcam_check_content_type(char *header) */ static int netcam_read_next_header(netcam_context_ptr netcam) { - int retval; - char *header; - - /* - * return if not connected - */ - if (netcam->sock == -1) return -1; - /* - * We are expecting a header which *must* contain a mime-type of - * image/jpeg, and *might* contain a Content-Length. - * - * If this is a "streaming" camera, the header *must* be preceded - * by a "boundary" string. - * - */ - netcam->caps.content_length = 0; - /* - * If this is a "streaming" camera, the stream header must be - * preceded by a "boundary" string - */ - if (netcam->caps.streaming == NCS_MULTIPART) { - while (1) { - retval = header_get(netcam, &header, HG_NONE); - - if (retval != HG_OK) { - /* Header reported as not-OK, check to see if it's null */ - if (strlen(header) == 0 ) { - if (debug_level > CAMERA_INFO) - motion_log(LOG_DEBUG, 0, "%s: Error reading image header, " - "streaming mode (1). Null header.", __FUNCTION__); - } else { - /* Header is not null. Output it in case it's a new camera with unknown headers. */ - if (debug_level > CAMERA_INFO) - motion_log(LOG_ERR, 0, "%s: Error reading image header, " - "streaming mode (1). Unknown header '%s'", - __FUNCTION__, header ); - } - - free(header); - return -1; - } - - retval = (strstr(header, netcam->boundary) == NULL); - free(header); - - if (!retval) - break; - } - } - - while (1) { - retval = header_get(netcam, &header, HG_NONE); - - if (retval != HG_OK) { - motion_log(LOG_ERR, 0, "%s: Error reading image header (2)", __FUNCTION__); - free(header); - return -1; - } - - if (*header == 0) - break; - - if ((retval = netcam_check_content_type(header)) >= 0) { - if (retval != 1) { - motion_log(LOG_ERR, 0, "%s: Header not JPEG", __FUNCTION__); - free(header); - return -1; - } - } - - if ((retval = (int) netcam_check_content_length(header)) > 0) { - netcam->caps.content_length = 1; /* set flag */ - netcam->receiving->content_length = (int) retval; - } - - free(header); - } - - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: Found image header record", __FUNCTION__); - - free(header); - return 0; + int retval; + char *header; + + /* + * return if not connected + */ + if (netcam->sock == -1) + return -1; + /* + * We are expecting a header which *must* contain a mime-type of + * image/jpeg, and *might* contain a Content-Length. + * + * If this is a "streaming" camera, the header *must* be preceded + * by a "boundary" string. + * + */ + netcam->caps.content_length = 0; + /* + * If this is a "streaming" camera, the stream header must be + * preceded by a "boundary" string + */ + if (netcam->caps.streaming == NCS_MULTIPART) { + while (1) { + retval = header_get(netcam, &header, HG_NONE); + + if (retval != HG_OK) { + /* Header reported as not-OK, check to see if it's null */ + if (strlen(header) == 0) { + if (debug_level > CAMERA_INFO) + motion_log(LOG_DEBUG, 0, "%s: Error reading image header, " + "streaming mode (1). Null header.", __FUNCTION__); + } else { + /* Header is not null. Output it in case it's a new camera with unknown headers. */ + if (debug_level > CAMERA_INFO) + motion_log(LOG_ERR, 0, "%s: Error reading image header, " + "streaming mode (1). Unknown header '%s'", + __FUNCTION__, header); + } + + free(header); + return -1; + } + + retval = (strstr(header, netcam->boundary) == NULL); + free(header); + + if (!retval) + break; + } + } + + while (1) { + retval = header_get(netcam, &header, HG_NONE); + + if (retval != HG_OK) { + motion_log(LOG_ERR, 0, "%s: Error reading image header (2)", __FUNCTION__); + free(header); + return -1; + } + + if (*header == 0) + break; + + if ((retval = netcam_check_content_type(header)) >= 0) { + if (retval != 1) { + motion_log(LOG_ERR, 0, "%s: Header not JPEG", __FUNCTION__); + free(header); + return -1; + } + } + + if ((retval = (int) netcam_check_content_length(header)) > 0) { + netcam->caps.content_length = 1; /* set flag */ + netcam->receiving->content_length = (int) retval; + } + + free(header); + } + + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: Found image header record", __FUNCTION__); + + free(header); + return 0; } /** @@ -538,240 +540,245 @@ static int netcam_read_next_header(netcam_context_ptr netcam) */ static int netcam_read_first_header(netcam_context_ptr netcam) { - int retval = -2; /* "Unknown err" */ - int ret; - int firstflag = 1; - int aliveflag = 0; /* If we have seen a Keep-Alive header from cam */ - int closeflag = 0; /* If we have seen a Connection: close header from cam */ - char *header; - char *boundary; - //struct context *cnt = netcam->cnt; /* for conf debug_level */ - - /* Send the initial command to the camera */ - if (send(netcam->sock, netcam->connect_request, - strlen(netcam->connect_request), 0) < 0) { - motion_log(LOG_ERR, 1, "%s: Error sending 'connect' request", __FUNCTION__); - return -1; - } - - /* - * We expect to get back an HTTP header from the camera. - * Successive calls to header_get will return each line - * of the header received. We will continue reading until - * a blank line is received. - * - * As we process the header, we are looking for either of - * header lines Content-type or Content-length. Content-type - * is used to determine whether the camera is "streaming" or - * "non-streaming", and Content-length will be used to determine - * whether future reads of images will be controlled by the - * length specified before the image, or by a boundary string. - * - * The Content-length will only be present "just before" an - * image is sent (if it is present at all). That means that, if - * this is a "streaming" camera, it will not be present in the - * "first header", but will occur later (after a boundary-string). - * For a non-streaming camera, however, there is no boundary-string, - * and the first header is, in fact, the only header. In this case, - * there may be a Content-length. - * - */ - while (1) { /* 'Do forever' */ - ret = header_get(netcam, &header, HG_NONE); - - if (debug_level > CAMERA_INFO) /* Changed criterion and moved up from below to catch headers that cause returns */ - motion_log(LOG_DEBUG, 0, "%s: Received first header ('%s')", __FUNCTION__, header); - - if (ret != HG_OK) { - if (debug_level > CAMERA_INFO) - motion_log(LOG_ERR, 0, "%s: Error reading first header (%s)", __FUNCTION__, header); - free(header); - return -1; - } - if (firstflag) { - if ((ret = http_result_code(header)) != 200) { - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: HTTP Result code %d", __FUNCTION__, ret); - free(header); - if (netcam->connect_keepalive) { - /* Cannot unset netcam->cnt->conf.netcam_keepalive as it is assigned const */ - /* But we do unset the netcam keepalive flag which was set in netcam_start */ - /* This message is logged as Information as it would be useful to know */ - /* if your netcam often returns bad HTTP result codes */ - netcam->connect_keepalive = 0; - motion_log(LOG_INFO, 0, "%s: Removed netcam Keep-Alive flag" - "due to apparent closed HTTP connection.", __FUNCTION__); - } - return ret; - } - firstflag = 0; - free(header); - continue; - } - if (*header == 0) /* blank line received */ - break; - - /* Check if this line is the content type */ - if ((ret = netcam_check_content_type(header)) >= 0) { - retval = ret; - /* - * We are expecting to find one of three types: - * 'multipart/x-mixed-replace', 'multipart/mixed' - * or 'image/jpeg'. The first two will be received - * from a streaming camera, and the third from a - * camera which provides a single frame only. - */ - switch (ret) { - case 1: /* not streaming */ - if (SETUP) { - if (netcam->connect_keepalive) - motion_log(LOG_DEBUG, 0, "%s: Non-streaming camera " - "(keep-alive set)", __FUNCTION__); - else - motion_log(LOG_DEBUG, 0, "%s: Non-streaming camera " - "(keep-alive not set)", __FUNCTION__); - } - netcam->caps.streaming = NCS_UNSUPPORTED; - break; - - case 2: /* streaming */ - if (SETUP) - motion_log(LOG_DEBUG, 0, "%s: Streaming camera", __FUNCTION__); - - netcam->caps.streaming = NCS_MULTIPART; - - if ((boundary = strstr(header, "boundary="))) { - /* - * on error recovery this - * may already be set - * */ - if (netcam->boundary) - free(netcam->boundary); - - netcam->boundary = mystrdup(boundary + 9); - /* - * HTTP protocol apparently permits the boundary string - * to be quoted (the Lumenera does this, which caused - * trouble) so we need to get rid of any surrounding - * quotes - */ - check_quote(netcam->boundary); - netcam->boundary_length = strlen(netcam->boundary); - - if (SETUP) { - motion_log(LOG_DEBUG, 0, "%s: Boundary string [%s]", - __FUNCTION__, netcam->boundary); - } - } - break; - case 3: /* MJPG-Block style streaming */ - if (SETUP) - motion_log(LOG_DEBUG, 0, "%s: Streaming camera probably using MJPG-blocks," - " consider using mjpg:// netcam_url.", __FUNCTION__); - break; - - default:{ /* error */ - motion_log(LOG_ERR, 0, "%s: Unrecognized content type", __FUNCTION__); - free(header); - return -1; - } - } - } else if ((ret = (int) netcam_check_content_length(header)) >= 0) { - if (SETUP) - motion_log(LOG_DEBUG, 0, "%s: Content-length present", __FUNCTION__); - - netcam->caps.content_length = 1; /* set flag */ - netcam->receiving->content_length = ret; - } else if (netcam_check_keepalive(header) == TRUE) { - /* Note that we have received a Keep-Alive header, and thus the socket can be left open */ - aliveflag = TRUE; - netcam->keepalive_thisconn = TRUE; - /* This flag will not be set when a Streaming cam is in use, but that */ - /* does not matter as the test below looks at Streaming state also. */ - } else if (netcam_check_close(header) == TRUE) { - /* Note that we have received a Connection: close header */ - closeflag = TRUE; - /* This flag is acted upon below */ - if (debug_level > CAMERA_INFO) /* Changed criterion and moved up from below to catch headers that cause returns */ - motion_log(LOG_DEBUG, 0, "%s: Found Conn: close header ('%s')", __FUNCTION__, header); - } - free(header); - } - free(header); - - if (netcam->caps.streaming == NCS_UNSUPPORTED && netcam->connect_keepalive) { - - /* - * If we are a non-streaming (ie. Jpeg) netcam and keepalive is configured - */ - - if (aliveflag) { - if (closeflag) { - /* - * If not a streaming cam, and keepalive is set, and the flag shows we - * did not see a Keep-Alive field returned from netcam and a Close field. - * Not quite sure what the correct course of action is here. In for testing. - */ - motion_log(LOG_INFO, 0, "%s: Info: Both 'Connection: Keep-Alive' and " - "'Connection: close' header received. Motion continues unchanged.", - __FUNCTION__); - } else { - /* aliveflag && !closeflag - * - * If not a streaming cam, and keepalive is set, and the flag shows we - * just got a Keep-Alive field returned from netcam and no Close field. - * No action, as this is the normal case. In debug we print a notification. - */ - - if (debug_level > CAMERA_INFO) - motion_log(LOG_INFO, 0, "%s: Info: Received a Keep-Alive field in this" - "set of headers.", __FUNCTION__); - } - } else { /* !aliveflag */ - if (!closeflag) { - /* - * If not a streaming cam, and keepalive is set, and the flag shows we - * did not see a Keep-Alive field returned from netcam nor a Close field. - * Not quite sure what the correct course of action is here. In for testing. - */ - motion_log(LOG_INFO, 0, "%s: Info: No 'Connection: Keep-Alive' nor 'Connection: close' " - "header received. Motion continues unchanged.", __FUNCTION__); - }else{ - /* !aliveflag & closeflag - * If not a streaming cam, and keepalive is set, and the flag shows we - * received a 'Connection: close' field returned from netcam. It is not likely - * we will get a Keep-Alive and Close header together - this is picked up by - * the test code above. - * If we receive a Close header, then we want to cease keep-alive for this cam. - * This situation will occur in 2 situations: - * (a) in HTTP 1.1 when the client wants to stop the keep-alive - * (and in this case it would be correct to close connection and then - * make a new one, with keep-alive set again). - * (b) in HTTP 1.0 with keepalive, when the client does not support it. - * In this case we should not attempt to re-start Keep-Alive. - * Due to that, we accept a Connection: close header in HTTP 1.0 & 1.1 modes - * - * To tell between the sitation where a camera has been in Keep-Alive mode and - * is now finishing (and will want to be re-started in Keep-Alive) and the other - * case when a cam does not support it, we have a flag which says if the netcam - * has returned a Keep-Alive flag during this connection. If that's set, we - * set ourselves up to re-connect with Keep-Alive after the socket is closed. - * If it's not set, then we will not try again to use Keep-Alive. - */ - if (!netcam->keepalive_thisconn) { - netcam->connect_keepalive = FALSE; /* No further attempts at keep-alive */ - motion_log(LOG_INFO, 0, "%s: Removed netcam Keep-Alive flag because " - "'Connection: close' header received. Netcam does not support " - "Keep-Alive. Motion continues in non-Keep-Alive.", __FUNCTION__); - } else { - netcam->keepalive_timeup = TRUE; /* We will close and re-open keep-alive */ - motion_log(LOG_INFO, 0, "Keep-Alive has reached end of valid period. Motion will close " - "netcam, then resume Keep-Alive with a new socket.", __FUNCTION__); - } - } - } - } - return retval; + int retval = -2; /* "Unknown err" */ + int ret; + int firstflag = 1; + int aliveflag = 0; /* If we have seen a Keep-Alive header from cam */ + int closeflag = 0; /* If we have seen a Connection: close header from cam */ + char *header; + char *boundary; + //struct context *cnt = netcam->cnt; /* for conf debug_level */ + + /* Send the initial command to the camera */ + if (send(netcam->sock, netcam->connect_request, + strlen(netcam->connect_request), 0) < 0) { + motion_log(LOG_ERR, 1, "%s: Error sending 'connect' request", __FUNCTION__); + return -1; + } + + /* + * We expect to get back an HTTP header from the camera. + * Successive calls to header_get will return each line + * of the header received. We will continue reading until + * a blank line is received. + * + * As we process the header, we are looking for either of + * header lines Content-type or Content-length. Content-type + * is used to determine whether the camera is "streaming" or + * "non-streaming", and Content-length will be used to determine + * whether future reads of images will be controlled by the + * length specified before the image, or by a boundary string. + * + * The Content-length will only be present "just before" an + * image is sent (if it is present at all). That means that, if + * this is a "streaming" camera, it will not be present in the + * "first header", but will occur later (after a boundary-string). + * For a non-streaming camera, however, there is no boundary-string, + * and the first header is, in fact, the only header. In this case, + * there may be a Content-length. + * + */ + while (1) { /* 'Do forever' */ + ret = header_get(netcam, &header, HG_NONE); + + if (debug_level > CAMERA_INFO) /* Changed criterion and moved up from below to catch headers that cause returns */ + motion_log(LOG_DEBUG, 0, "%s: Received first header ('%s')", __FUNCTION__, header); + + if (ret != HG_OK) { + if (debug_level > CAMERA_INFO) + motion_log(LOG_ERR, 0, "%s: Error reading first header (%s)", __FUNCTION__, header); + free(header); + return -1; + } + + if (firstflag) { + if ((ret = http_result_code(header)) != 200) { + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: HTTP Result code %d", __FUNCTION__, ret); + + free(header); + if (netcam->connect_keepalive) { + /* Cannot unset netcam->cnt->conf.netcam_keepalive as it is assigned const */ + /* But we do unset the netcam keepalive flag which was set in netcam_start */ + /* This message is logged as Information as it would be useful to know */ + /* if your netcam often returns bad HTTP result codes */ + netcam->connect_keepalive = 0; + motion_log(LOG_INFO, 0, "%s: Removed netcam Keep-Alive flag" + "due to apparent closed HTTP connection.", __FUNCTION__); + } + return ret; + } + firstflag = 0; + free(header); + continue; + } + + if (*header == 0) /* blank line received */ + break; + + /* Check if this line is the content type */ + if ((ret = netcam_check_content_type(header)) >= 0) { + retval = ret; + /* + * We are expecting to find one of three types: + * 'multipart/x-mixed-replace', 'multipart/mixed' + * or 'image/jpeg'. The first two will be received + * from a streaming camera, and the third from a + * camera which provides a single frame only. + */ + switch (ret) { + case 1: /* not streaming */ + if (SETUP) { + if (netcam->connect_keepalive) + motion_log(LOG_DEBUG, 0, "%s: Non-streaming camera " + "(keep-alive set)", __FUNCTION__); + else + motion_log(LOG_DEBUG, 0, "%s: Non-streaming camera " + "(keep-alive not set)", __FUNCTION__); + } + netcam->caps.streaming = NCS_UNSUPPORTED; + break; + + case 2: /* streaming */ + if (SETUP) + motion_log(LOG_DEBUG, 0, "%s: Streaming camera", __FUNCTION__); + + netcam->caps.streaming = NCS_MULTIPART; + + if ((boundary = strstr(header, "boundary="))) { + /* + * on error recovery this + * may already be set + * */ + if (netcam->boundary) + free(netcam->boundary); + + netcam->boundary = mystrdup(boundary + 9); + /* + * HTTP protocol apparently permits the boundary string + * to be quoted (the Lumenera does this, which caused + * trouble) so we need to get rid of any surrounding + * quotes + */ + check_quote(netcam->boundary); + netcam->boundary_length = strlen(netcam->boundary); + + if (SETUP) + motion_log(LOG_DEBUG, 0, "%s: Boundary string [%s]", + __FUNCTION__, netcam->boundary); + + } + break; + case 3: /* MJPG-Block style streaming */ + if (SETUP) + motion_log(LOG_DEBUG, 0, "%s: Streaming camera probably using MJPG-blocks," + " consider using mjpg:// netcam_url.", __FUNCTION__); + break; + + default: + /* error */ + motion_log(LOG_ERR, 0, "%s: Unrecognized content type", __FUNCTION__); + free(header); + return -1; + + } + } else if ((ret = (int) netcam_check_content_length(header)) >= 0) { + if (SETUP) + motion_log(LOG_DEBUG, 0, "%s: Content-length present", __FUNCTION__); + + netcam->caps.content_length = 1; /* set flag */ + netcam->receiving->content_length = ret; + } else if (netcam_check_keepalive(header) == TRUE) { + /* Note that we have received a Keep-Alive header, and thus the socket can be left open */ + aliveflag = TRUE; + netcam->keepalive_thisconn = TRUE; + /* This flag will not be set when a Streaming cam is in use, but that */ + /* does not matter as the test below looks at Streaming state also. */ + } else if (netcam_check_close(header) == TRUE) { + /* Note that we have received a Connection: close header */ + closeflag = TRUE; + /* This flag is acted upon below */ + /* Changed criterion and moved up from below to catch headers that cause returns */ + if (debug_level > CAMERA_INFO) + motion_log(LOG_DEBUG, 0, "%s: Found Conn: close header ('%s')", __FUNCTION__, header); + } + free(header); + } + free(header); + + if (netcam->caps.streaming == NCS_UNSUPPORTED && netcam->connect_keepalive) { + + /* + * If we are a non-streaming (ie. Jpeg) netcam and keepalive is configured + */ + + if (aliveflag) { + if (closeflag) { + /* + * If not a streaming cam, and keepalive is set, and the flag shows we + * did not see a Keep-Alive field returned from netcam and a Close field. + * Not quite sure what the correct course of action is here. In for testing. + */ + motion_log(LOG_INFO, 0, "%s: Info: Both 'Connection: Keep-Alive' and " + "'Connection: close' header received. Motion continues unchanged.", + __FUNCTION__); + } else { + /* aliveflag && !closeflag + * + * If not a streaming cam, and keepalive is set, and the flag shows we + * just got a Keep-Alive field returned from netcam and no Close field. + * No action, as this is the normal case. In debug we print a notification. + */ + + if (debug_level > CAMERA_INFO) + motion_log(LOG_INFO, 0, "%s: Info: Received a Keep-Alive field in this" + "set of headers.", __FUNCTION__); + } + } else { /* !aliveflag */ + if (!closeflag) { + /* + * If not a streaming cam, and keepalive is set, and the flag shows we + * did not see a Keep-Alive field returned from netcam nor a Close field. + * Not quite sure what the correct course of action is here. In for testing. + */ + motion_log(LOG_INFO, 0, "%s: Info: No 'Connection: Keep-Alive' nor 'Connection: close' " + "header received. Motion continues unchanged.", __FUNCTION__); + } else { + /* !aliveflag & closeflag + * If not a streaming cam, and keepalive is set, and the flag shows we + * received a 'Connection: close' field returned from netcam. It is not likely + * we will get a Keep-Alive and Close header together - this is picked up by + * the test code above. + * If we receive a Close header, then we want to cease keep-alive for this cam. + * This situation will occur in 2 situations: + * (a) in HTTP 1.1 when the client wants to stop the keep-alive + * (and in this case it would be correct to close connection and then + * make a new one, with keep-alive set again). + * (b) in HTTP 1.0 with keepalive, when the client does not support it. + * In this case we should not attempt to re-start Keep-Alive. + * Due to that, we accept a Connection: close header in HTTP 1.0 & 1.1 modes + * + * To tell between the sitation where a camera has been in Keep-Alive mode and + * is now finishing (and will want to be re-started in Keep-Alive) and the other + * case when a cam does not support it, we have a flag which says if the netcam + * has returned a Keep-Alive flag during this connection. If that's set, we + * set ourselves up to re-connect with Keep-Alive after the socket is closed. + * If it's not set, then we will not try again to use Keep-Alive. + */ + if (!netcam->keepalive_thisconn) { + netcam->connect_keepalive = FALSE; /* No further attempts at keep-alive */ + motion_log(LOG_INFO, 0, "%s: Removed netcam Keep-Alive flag because " + "'Connection: close' header received. Netcam does not support " + "Keep-Alive. Motion continues in non-Keep-Alive.", __FUNCTION__); + } else { + netcam->keepalive_timeup = TRUE; /* We will close and re-open keep-alive */ + motion_log(LOG_INFO, 0, "Keep-Alive has reached end of valid period. Motion will close " + "netcam, then resume Keep-Alive with a new socket.", __FUNCTION__); + } + } + } + } + return retval; } /** @@ -788,12 +795,12 @@ static int netcam_read_first_header(netcam_context_ptr netcam) */ static void netcam_disconnect(netcam_context_ptr netcam) { - if (netcam->sock > 0) { - if (close(netcam->sock) < 0) - motion_log(LOG_ERR, 1, "%s: netcam_disconnect", __FUNCTION__); + if (netcam->sock > 0) { + if (close(netcam->sock) < 0) + motion_log(LOG_ERR, 1, "%s: netcam_disconnect", __FUNCTION__); - netcam->sock = -1; - } + netcam->sock = -1; + } } /** @@ -815,182 +822,186 @@ static void netcam_disconnect(netcam_context_ptr netcam) */ static int netcam_connect(netcam_context_ptr netcam, int err_flag) { - struct sockaddr_in server; /* for connect */ - struct addrinfo *res; /* for getaddrinfo */ - int ret; - int saveflags; - int back_err; - int optval; - socklen_t optlen = sizeof(optval); - socklen_t len; - fd_set fd_w; - struct timeval selecttime; - - /* Assure any previous connection has been closed - IF we are not in keepalive */ - if (!netcam->connect_keepalive) { - if (debug_level > CAMERA_INFO) - motion_log(LOG_DEBUG, 0, "%s: disconnecting netcam " - "since keep-alive not set.", __FUNCTION__); - netcam_disconnect(netcam); - - /* create a new socket */ - if ((netcam->sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { - motion_log(LOG_ERR, 1, "%s: with no keepalive, attempt " - "to create socket failed.", __FUNCTION__); - return -1; - } - if (debug_level > CAMERA_INFO) - motion_log(LOG_DEBUG, 0, "%s: with no keepalive, " - "new socket created fd %d", __FUNCTION__, netcam->sock); - - } else if (netcam->sock == -1) { /* We are in keepalive mode, check for invalid socket */ - /* Must be first time, or closed, create a new socket */ - if ((netcam->sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { - motion_log(LOG_ERR, 1, "%s: with keepalive set, invalid socket." - "This could be the first time. Creating a new one failed.", - __FUNCTION__); - return -1; - } - if (debug_level > CAMERA_INFO) - motion_log(LOG_DEBUG, 0, "%s: with keepalive set, invalid socket." - "This could be first time, created a new one with fd %d", - __FUNCTION__, netcam->sock); - - /* Record that this connection has not yet received a Keep-Alive header */ - netcam->keepalive_thisconn = FALSE; - - /* Check the socket status for the keepalive option */ - if (getsockopt(netcam->sock, SOL_SOCKET, SO_KEEPALIVE, &optval, &optlen) < 0) { - motion_log(LOG_ERR, 1, "%s: getsockopt()", __FUNCTION__); - return -1; - } - if (debug_level > CAMERA_INFO) - motion_log(LOG_DEBUG, 0, "%s: SO_KEEPALIVE is %s", __FUNCTION__, optval ? "ON":"OFF"); - - /* Set the option active */ - optval = 1; - optlen = sizeof(optval); - if (setsockopt(netcam->sock, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) { - motion_log(LOG_ERR, 1, "%s: setsockopt()", __FUNCTION__); - return -1; - } - if (debug_level > CAMERA_INFO ) - motion_log(LOG_DEBUG, 0, "%s: SO_KEEPALIVE set on socket.", __FUNCTION__); - - } else if (debug_level > CAMERA_INFO ) { - motion_log(LOG_DEBUG, 0, "%s: re-using socket %d since keepalive is set.", - __FUNCTION__, netcam->sock); - } - - - /* lookup the hostname given in the netcam URL */ - if ((ret = getaddrinfo(netcam->connect_host, NULL, NULL, &res)) != 0) { - if (!err_flag) - motion_log(LOG_ERR, 0, "%s: getaddrinfo() failed (%s): %s", - __FUNCTION__, netcam->connect_host, gai_strerror(ret)); - - if (debug_level > CAMERA_INFO) - motion_log(LOG_DEBUG, 0, "%s: disconnecting netcam (1)", __FUNCTION__); - - netcam_disconnect(netcam); - return -1; - } - - /* - * Fill the hostname details into the 'server' structure and - * attempt to connect to the remote server - */ - memset(&server, 0, sizeof(server)); - memcpy(&server, res->ai_addr, sizeof(server)); - freeaddrinfo(res); - - server.sin_family = AF_INET; - server.sin_port = htons(netcam->connect_port); - - /* - * We set the socket non-blocking and then use a 'select' - * system call to control the timeout. - */ - - if ((saveflags = fcntl(netcam->sock, F_GETFL, 0)) < 0) { - motion_log(LOG_ERR, 1, "%s: fcntl(1) on socket", __FUNCTION__); - netcam_disconnect(netcam); - return -1; - } - - /* Set the socket non-blocking */ - if (fcntl(netcam->sock, F_SETFL, saveflags | O_NONBLOCK) < 0) { - motion_log(LOG_ERR, 1, "%s: fcntl(2) on socket", __FUNCTION__); - netcam_disconnect(netcam); - return -1; - } - - /* Now the connect call will return immediately */ - ret = connect(netcam->sock, (struct sockaddr *) &server, - sizeof(server)); - back_err = errno; /* save the errno from connect */ - - /* If the connect failed with anything except EINPROGRESS, error */ - if ((ret < 0) && (back_err != EINPROGRESS)) { - if (!err_flag) - motion_log(LOG_ERR, 1, "%s: connect() failed (%d)", - __FUNCTION__, back_err); - - if (debug_level > CAMERA_INFO) - motion_log(LOG_DEBUG, 0, "%s: disconnecting netcam (4)", - __FUNCTION__); - - netcam_disconnect(netcam); - return -1; - } - - /* Now we do a 'select' with timeout to wait for the connect */ - FD_ZERO(&fd_w); - FD_SET(netcam->sock, &fd_w); - selecttime.tv_sec = CONNECT_TIMEOUT; - selecttime.tv_usec = 0; - ret = select(FD_SETSIZE, NULL, &fd_w, NULL, &selecttime); - - if (ret == 0) { /* 0 means timeout */ - if (!err_flag) - motion_log(LOG_ERR, 0, "%s: timeout on connect()", __FUNCTION__); - - if (debug_level > CAMERA_INFO) - motion_log(LOG_DEBUG, 0, "%s: disconnecting netcam (2)", __FUNCTION__); - - netcam_disconnect(netcam); - return -1; - } - - /* - * A +ve value returned from the select (actually, it must be a - * '1' showing 1 fd's changed) shows the select has completed. - * Now we must check the return code from the select. - */ - len = sizeof(ret); - - if (getsockopt(netcam->sock, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { - motion_log(LOG_ERR, 0, "%s: getsockopt after connect", __FUNCTION__); - netcam_disconnect(netcam); - return -1; - } - - /* If the return code is anything except 0, error on connect */ - if (ret) { - if (!err_flag) - motion_log(LOG_ERR, 1, "%s: connect returned error", __FUNCTION__); - - if (debug_level > CAMERA_INFO) - motion_log(LOG_DEBUG, 0, "%s: disconnecting netcam (3)", __FUNCTION__); - - netcam_disconnect(netcam); - return -1; - } - - /* The socket info is stored in the rbuf structure of our context */ - rbuf_initialize(netcam); - - return 0; /* success */ + struct sockaddr_in server; /* for connect */ + struct addrinfo *res; /* for getaddrinfo */ + int ret; + int saveflags; + int back_err; + int optval; + socklen_t optlen = sizeof(optval); + socklen_t len; + fd_set fd_w; + struct timeval selecttime; + + /* Assure any previous connection has been closed - IF we are not in keepalive */ + if (!netcam->connect_keepalive) { + if (debug_level > CAMERA_INFO) + motion_log(LOG_DEBUG, 0, "%s: disconnecting netcam " + "since keep-alive not set.", __FUNCTION__); + netcam_disconnect(netcam); + + /* create a new socket */ + if ((netcam->sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { + motion_log(LOG_ERR, 1, "%s: with no keepalive, attempt " + "to create socket failed.", __FUNCTION__); + return -1; + } + if (debug_level > CAMERA_INFO) + motion_log(LOG_DEBUG, 0, "%s: with no keepalive, " + "new socket created fd %d", __FUNCTION__, netcam->sock); + + } else if (netcam->sock == -1) { /* We are in keepalive mode, check for invalid socket */ + /* Must be first time, or closed, create a new socket */ + if ((netcam->sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { + motion_log(LOG_ERR, 1, "%s: with keepalive set, invalid socket." + "This could be the first time. Creating a new one failed.", + __FUNCTION__); + return -1; + } + + if (debug_level > CAMERA_INFO) + motion_log(LOG_DEBUG, 0, "%s: with keepalive set, invalid socket." + "This could be first time, created a new one with fd %d", + __FUNCTION__, netcam->sock); + + /* Record that this connection has not yet received a Keep-Alive header */ + netcam->keepalive_thisconn = FALSE; + + /* Check the socket status for the keepalive option */ + if (getsockopt(netcam->sock, SOL_SOCKET, SO_KEEPALIVE, &optval, &optlen) < 0) { + motion_log(LOG_ERR, 1, "%s: getsockopt()", __FUNCTION__); + return -1; + } + + if (debug_level > CAMERA_INFO) + motion_log(LOG_DEBUG, 0, "%s: SO_KEEPALIVE is %s", __FUNCTION__, optval ? "ON":"OFF"); + + /* Set the option active */ + optval = 1; + optlen = sizeof(optval); + + if (setsockopt(netcam->sock, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) { + motion_log(LOG_ERR, 1, "%s: setsockopt()", __FUNCTION__); + return -1; + } + + if (debug_level > CAMERA_INFO) + motion_log(LOG_DEBUG, 0, "%s: SO_KEEPALIVE set on socket.", __FUNCTION__); + + } else if (debug_level > CAMERA_INFO) { + motion_log(LOG_DEBUG, 0, "%s: re-using socket %d since keepalive is set.", + __FUNCTION__, netcam->sock); + } + + + /* lookup the hostname given in the netcam URL */ + if ((ret = getaddrinfo(netcam->connect_host, NULL, NULL, &res)) != 0) { + if (!err_flag) + motion_log(LOG_ERR, 0, "%s: getaddrinfo() failed (%s): %s", + __FUNCTION__, netcam->connect_host, gai_strerror(ret)); + + if (debug_level > CAMERA_INFO) + motion_log(LOG_DEBUG, 0, "%s: disconnecting netcam (1)", __FUNCTION__); + + netcam_disconnect(netcam); + return -1; + } + + /* + * Fill the hostname details into the 'server' structure and + * attempt to connect to the remote server + */ + memset(&server, 0, sizeof(server)); + memcpy(&server, res->ai_addr, sizeof(server)); + freeaddrinfo(res); + + server.sin_family = AF_INET; + server.sin_port = htons(netcam->connect_port); + + /* + * We set the socket non-blocking and then use a 'select' + * system call to control the timeout. + */ + + if ((saveflags = fcntl(netcam->sock, F_GETFL, 0)) < 0) { + motion_log(LOG_ERR, 1, "%s: fcntl(1) on socket", __FUNCTION__); + netcam_disconnect(netcam); + return -1; + } + + /* Set the socket non-blocking */ + if (fcntl(netcam->sock, F_SETFL, saveflags | O_NONBLOCK) < 0) { + motion_log(LOG_ERR, 1, "%s: fcntl(2) on socket", __FUNCTION__); + netcam_disconnect(netcam); + return -1; + } + + /* Now the connect call will return immediately */ + ret = connect(netcam->sock, (struct sockaddr *) &server, + sizeof(server)); + back_err = errno; /* save the errno from connect */ + + /* If the connect failed with anything except EINPROGRESS, error */ + if ((ret < 0) && (back_err != EINPROGRESS)) { + if (!err_flag) + motion_log(LOG_ERR, 1, "%s: connect() failed (%d)", + __FUNCTION__, back_err); + + if (debug_level > CAMERA_INFO) + motion_log(LOG_DEBUG, 0, "%s: disconnecting netcam (4)", + __FUNCTION__); + + netcam_disconnect(netcam); + return -1; + } + + /* Now we do a 'select' with timeout to wait for the connect */ + FD_ZERO(&fd_w); + FD_SET(netcam->sock, &fd_w); + selecttime.tv_sec = CONNECT_TIMEOUT; + selecttime.tv_usec = 0; + ret = select(FD_SETSIZE, NULL, &fd_w, NULL, &selecttime); + + if (ret == 0) { /* 0 means timeout */ + if (!err_flag) + motion_log(LOG_ERR, 0, "%s: timeout on connect()", __FUNCTION__); + + if (debug_level > CAMERA_INFO) + motion_log(LOG_DEBUG, 0, "%s: disconnecting netcam (2)", __FUNCTION__); + + netcam_disconnect(netcam); + return -1; + } + + /* + * A +ve value returned from the select (actually, it must be a + * '1' showing 1 fd's changed) shows the select has completed. + * Now we must check the return code from the select. + */ + len = sizeof(ret); + + if (getsockopt(netcam->sock, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { + motion_log(LOG_ERR, 0, "%s: getsockopt after connect", __FUNCTION__); + netcam_disconnect(netcam); + return -1; + } + + /* If the return code is anything except 0, error on connect */ + if (ret) { + if (!err_flag) + motion_log(LOG_ERR, 1, "%s: connect returned error", __FUNCTION__); + + if (debug_level > CAMERA_INFO) + motion_log(LOG_DEBUG, 0, "%s: disconnecting netcam (3)", __FUNCTION__); + + netcam_disconnect(netcam); + return -1; + } + + /* The socket info is stored in the rbuf structure of our context */ + rbuf_initialize(netcam); + + return 0; /* success */ } @@ -1009,29 +1020,29 @@ static int netcam_connect(netcam_context_ptr netcam, int err_flag) */ static void netcam_check_buffsize(netcam_buff_ptr buff, size_t numbytes) { - int min_size_to_alloc; - int real_alloc; - int new_size; + int min_size_to_alloc; + int real_alloc; + int new_size; - if ((buff->size - buff->used) >= numbytes) - return; + if ((buff->size - buff->used) >= numbytes) + return; - min_size_to_alloc = numbytes - (buff->size - buff->used); - real_alloc = ((min_size_to_alloc / NETCAM_BUFFSIZE) * NETCAM_BUFFSIZE); + min_size_to_alloc = numbytes - (buff->size - buff->used); + real_alloc = ((min_size_to_alloc / NETCAM_BUFFSIZE) * NETCAM_BUFFSIZE); - if ((min_size_to_alloc - real_alloc) > 0) - real_alloc += NETCAM_BUFFSIZE; + if ((min_size_to_alloc - real_alloc) > 0) + real_alloc += NETCAM_BUFFSIZE; - new_size = buff->size + real_alloc; - - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: expanding buffer from [%d/%d] to [%d/%d] bytes.", - __FUNCTION__, (int) buff->used, (int) buff->size, - (int) buff->used, new_size); + new_size = buff->size + real_alloc; + + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: expanding buffer from [%d/%d] to [%d/%d] bytes.", + __FUNCTION__, (int) buff->used, (int) buff->size, + (int) buff->used, new_size); - buff->ptr = myrealloc(buff->ptr, new_size, - "netcam_check_buf_size"); - buff->size = new_size; + buff->ptr = myrealloc(buff->ptr, new_size, + "netcam_check_buf_size"); + buff->size = new_size; } /** @@ -1042,10 +1053,10 @@ static void netcam_check_buffsize(netcam_buff_ptr buff, size_t numbytes) * * This routine is called under the four variations of two different * conditions: - * 1) Streaming or non-streaming camera - * Note: Keep-Alive is supported for non-streaming cameras, - * if enabled in the netcam's config structure. - * 2) Header does or does not include Content-Length + * 1) Streaming or non-streaming camera + * Note: Keep-Alive is supported for non-streaming cameras, + * if enabled in the netcam's config structure. + * 2) Header does or does not include Content-Length * Additionally, if it is a streaming camera, there must always be a * boundary-string. * @@ -1059,27 +1070,27 @@ static void netcam_check_buffsize(netcam_buff_ptr buff, size_t numbytes) * acted upon. * * Our algorithm for this will be as follows: - * 1) If a Content-Length is present, set the variable "remaining" - * to be equal to that value, else set it to a "very large" - * number. - * 2) While there is more data available from the camera: - * a) If there is a "boundary string" specified (from the initial - * header): - * i) If the amount of data in the input buffer is less than - * the length of the boundary string, get more data into - * the input buffer (error if failure). - * ii) If the boundary string is found, check how many - * characters remain in the input buffer before the start - * of the boundary string. If that is less than the - * variable "remaining", reset "remaining" to be equal - * to that number. - * b) Try to copy up to "remaining" characters from the input - * buffer into our destination buffer. - * c) If there are no more characters available from the camera, - * exit this loop, else subtract the number of characters - * actually copied from the variable "remaining". - * 3) If Content-Length was present, and "remaining" is not equal - * to zero, generate a warning message for logging. + * 1) If a Content-Length is present, set the variable "remaining" + * to be equal to that value, else set it to a "very large" + * number. + * 2) While there is more data available from the camera: + * a) If there is a "boundary string" specified (from the initial + * header): + * i) If the amount of data in the input buffer is less than + * the length of the boundary string, get more data into + * the input buffer (error if failure). + * ii) If the boundary string is found, check how many + * characters remain in the input buffer before the start + * of the boundary string. If that is less than the + * variable "remaining", reset "remaining" to be equal + * to that number. + * b) Try to copy up to "remaining" characters from the input + * buffer into our destination buffer. + * c) If there are no more characters available from the camera, + * exit this loop, else subtract the number of characters + * actually copied from the variable "remaining". + * 3) If Content-Length was present, and "remaining" is not equal + * to zero, generate a warning message for logging. * * * Parameters: @@ -1091,251 +1102,251 @@ static void netcam_check_buffsize(netcam_buff_ptr buff, size_t numbytes) */ static int netcam_read_html_jpeg(netcam_context_ptr netcam) { - netcam_buff_ptr buffer; - size_t remaining; /* # characters to read */ - size_t maxflush; /* # chars before boundary */ - size_t rem, rlen, ix; /* working vars */ - int retval; - char *ptr, *bptr, *rptr; - netcam_buff *xchg; - struct timeval curtime; - /* - * Initialisation - set our local pointers to the context - * information - */ - buffer = netcam->receiving; - /* Assure the target buffer is empty */ - buffer->used = 0; - /* Prepare for read loop */ - if (buffer->content_length != 0) - remaining = buffer->content_length; - else - remaining = 999999; - - /* Now read in the data */ - while (remaining) { - /* Assure data in input buffer */ - if (netcam->response->buffer_left <= 0) { - retval = rbuf_read_bufferful(netcam); - - if (retval <= 0) - break; - - netcam->response->buffer_left = retval; - netcam->response->buffer_pos = netcam->response->buffer; - } - - /* If a boundary string is present, take it into account */ - bptr = netcam->boundary; - - if (bptr) { - rptr = netcam->response->buffer_pos; - rlen = netcam->response->buffer_left; - - /* Loop through buffer looking for start of boundary */ - while (1) { - /* - * Logic gets a little complicated here. The - * problem is that we are reading in input - * data in packets, and there is a (small) - * chance that the boundary string could be - * split across successive packets. - * First a quick check if the string *might* - * be in the current buffer. - */ - if (rlen > remaining) - rlen = remaining; - - if (remaining < netcam->boundary_length) - break; - - if ((ptr = memchr(rptr, *bptr, rlen)) == NULL) - /* boundary not here (normal path) */ - break; - /* - * At least the first char was found in the - * buffer - check for the rest - */ - rem = rlen - (ptr - rptr); - for (ix = 1; (ix < rem) && (ix < netcam->boundary_length); ix++) { - if (ptr[ix] != bptr[ix]) - break; - } - - if ((ix != netcam->boundary_length) && (ix != rem)) { - /* - * Not pointing at a boundary string - - * step along input - */ - ix = ptr - rptr + 1; - rptr += ix; - rlen -= ix; - - if (rlen <= 0) - /* boundary not in buffer - go copy out - */ - break; - /* - * not yet decided - continue - * through input - */ - continue; - } - /* - * If we finish the 'for' with - * ix == boundary_length, that means we found - * the string, and should copy any data which - * precedes it into the target buffer, then - * exit the main loop. - */ - if (ix == netcam->boundary_length) { - if ((ptr - netcam->response->buffer) < (int) remaining) - remaining = ptr - netcam->response->buffer; - - /* go copy everything up to boundary */ - break; - } - - /* - * If not, and ix == rem, that means we reached - * the end of the input buffer in the middle of - * our check, which is the (somewhat messy) - * problem mentioned above. - * - * Assure there is data before potential - * boundary string - */ - if (ptr != netcam->response->buffer) { - /* - * We have a boundary string crossing - * packets :-(. We will copy all the - * data up to the beginning of the - * potential boundary, then re-position - * the (partial) string to the - * beginning and get some more input - * data. First we flush the input - * buffer up to the beginning of the - * (potential) boundary string - */ - ix = ptr - netcam->response->buffer_pos; - netcam_check_buffsize(buffer, ix); - retval = rbuf_flush(netcam, buffer->ptr + buffer->used, ix); - buffer->used += retval; - remaining -= retval; - - /* - * Now move the boundary fragment to - * the head of the input buffer. - * This is really a "hack" - ideally, - * we should have a function within the - * module netcam_wget.c to do this job! - */ - - if (debug_level > CAMERA_INFO) { - motion_log(-1, 0, - "%s: Potential split boundary - " - "%d chars flushed, %d " - "re-positioned", __FUNCTION__, ix, - (int) netcam->response->buffer_left); - } - - memmove(netcam->response->buffer, ptr, - netcam->response->buffer_left); - } /* end of boundary split over buffer */ - - retval = netcam_recv(netcam, netcam->response->buffer + - netcam->response->buffer_left, - sizeof(netcam->response->buffer) - - netcam->response->buffer_left); - - if (retval <= 0) { /* this is a fatal error */ - motion_log(LOG_ERR, 1, "%s: recv() fail after boundary string", - __FUNCTION__); - return -1; - } - - /* Reset the input buffer pointers */ - netcam->response->buffer_left = retval + netcam->response->buffer_left; - netcam->response->buffer_pos = netcam->response->buffer; - - /* This will cause a 'continue' of the main loop */ - bptr = NULL; - - /* Return to do the boundary compare from the start */ - break; - } /* end of while(1) input buffer search */ - - /* !bptr shows we're processing split boundary */ - if (!bptr) - continue; - } /* end of if(bptr) */ - - /* boundary string not present, so just write out as much data as possible */ - if (remaining) { - maxflush = MINVAL(netcam->response->buffer_left, remaining); - netcam_check_buffsize(buffer, maxflush); - retval = rbuf_flush(netcam, buffer->ptr + buffer->used, maxflush); - buffer->used += retval; - remaining -= retval; - } - } - - /* - * read is complete - set the current 'receiving' buffer atomically - * as 'latest', and make the buffer previously in 'latest' become - * the new 'receiving' - */ - if (gettimeofday(&curtime, NULL) < 0) { - motion_log(LOG_ERR, 1, "%s: gettimeofday in netcam_read_jpeg", __FUNCTION__); - } - - netcam->receiving->image_time = curtime; - - /* - * Calculate our "running average" time for this netcam's - * frame transmissions (except for the first time). - * Note that the average frame time is held in microseconds. - */ - if (netcam->last_image.tv_sec) { - netcam->av_frame_time = (9.0 * netcam->av_frame_time + - 1000000.0 * (curtime.tv_sec - netcam->last_image.tv_sec) + - (curtime.tv_usec- netcam->last_image.tv_usec)) / 10.0; - - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: Calculated frame time %f", - __FUNCTION__, netcam->av_frame_time); - } - netcam->last_image = curtime; - - pthread_mutex_lock(&netcam->mutex); - - xchg = netcam->latest; - netcam->latest = netcam->receiving; - netcam->receiving = xchg; - netcam->imgcnt++; - /* - * We have a new frame ready. We send a signal so that - * any thread (e.g. the motion main loop) waiting for the - * next frame to become available may proceed. - */ - pthread_cond_signal(&netcam->pic_ready); - - pthread_mutex_unlock(&netcam->mutex); - - if (netcam->caps.streaming == NCS_UNSUPPORTED) { - if (!netcam->connect_keepalive) { - if (debug_level > CAMERA_INFO ) - motion_log(LOG_DEBUG, 0, "%s: netcam_read_html_jpeg disconnecting " - "netcam since keep-alive not set.", __FUNCTION__); - - netcam_disconnect(netcam); - } else if (debug_level > CAMERA_INFO ) { - motion_log(LOG_DEBUG, 0, "%s: netcam_read_html_jpeg leaving netcam connected.", - __FUNCTION__); - } - } - - return 0; + netcam_buff_ptr buffer; + size_t remaining; /* # characters to read */ + size_t maxflush; /* # chars before boundary */ + size_t rem, rlen, ix; /* working vars */ + int retval; + char *ptr, *bptr, *rptr; + netcam_buff *xchg; + struct timeval curtime; + /* + * Initialisation - set our local pointers to the context + * information + */ + buffer = netcam->receiving; + /* Assure the target buffer is empty */ + buffer->used = 0; + /* Prepare for read loop */ + if (buffer->content_length != 0) + remaining = buffer->content_length; + else + remaining = 999999; + + /* Now read in the data */ + while (remaining) { + /* Assure data in input buffer */ + if (netcam->response->buffer_left <= 0) { + retval = rbuf_read_bufferful(netcam); + + if (retval <= 0) + break; + + netcam->response->buffer_left = retval; + netcam->response->buffer_pos = netcam->response->buffer; + } + + /* If a boundary string is present, take it into account */ + bptr = netcam->boundary; + + if (bptr) { + rptr = netcam->response->buffer_pos; + rlen = netcam->response->buffer_left; + + /* Loop through buffer looking for start of boundary */ + while (1) { + /* + * Logic gets a little complicated here. The + * problem is that we are reading in input + * data in packets, and there is a (small) + * chance that the boundary string could be + * split across successive packets. + * First a quick check if the string *might* + * be in the current buffer. + */ + if (rlen > remaining) + rlen = remaining; + + if (remaining < netcam->boundary_length) + break; + + if ((ptr = memchr(rptr, *bptr, rlen)) == NULL) + /* boundary not here (normal path) */ + break; + /* + * At least the first char was found in the + * buffer - check for the rest + */ + rem = rlen - (ptr - rptr); + for (ix = 1; (ix < rem) && (ix < netcam->boundary_length); ix++) { + if (ptr[ix] != bptr[ix]) + break; + } + + if ((ix != netcam->boundary_length) && (ix != rem)) { + /* + * Not pointing at a boundary string - + * step along input + */ + ix = ptr - rptr + 1; + rptr += ix; + rlen -= ix; + + if (rlen <= 0) + /* boundary not in buffer - go copy out + */ + break; + /* + * not yet decided - continue + * through input + */ + continue; + } + /* + * If we finish the 'for' with + * ix == boundary_length, that means we found + * the string, and should copy any data which + * precedes it into the target buffer, then + * exit the main loop. + */ + if (ix == netcam->boundary_length) { + if ((ptr - netcam->response->buffer) < (int) remaining) + remaining = ptr - netcam->response->buffer; + + /* go copy everything up to boundary */ + break; + } + + /* + * If not, and ix == rem, that means we reached + * the end of the input buffer in the middle of + * our check, which is the (somewhat messy) + * problem mentioned above. + * + * Assure there is data before potential + * boundary string + */ + if (ptr != netcam->response->buffer) { + /* + * We have a boundary string crossing + * packets :-(. We will copy all the + * data up to the beginning of the + * potential boundary, then re-position + * the (partial) string to the + * beginning and get some more input + * data. First we flush the input + * buffer up to the beginning of the + * (potential) boundary string + */ + ix = ptr - netcam->response->buffer_pos; + netcam_check_buffsize(buffer, ix); + retval = rbuf_flush(netcam, buffer->ptr + buffer->used, ix); + buffer->used += retval; + remaining -= retval; + + /* + * Now move the boundary fragment to + * the head of the input buffer. + * This is really a "hack" - ideally, + * we should have a function within the + * module netcam_wget.c to do this job! + */ + + if (debug_level > CAMERA_INFO) { + motion_log(-1, 0, + "%s: Potential split boundary - " + "%d chars flushed, %d " + "re-positioned", __FUNCTION__, ix, + (int) netcam->response->buffer_left); + } + + memmove(netcam->response->buffer, ptr, + netcam->response->buffer_left); + } /* end of boundary split over buffer */ + + retval = netcam_recv(netcam, netcam->response->buffer + + netcam->response->buffer_left, + sizeof(netcam->response->buffer) - + netcam->response->buffer_left); + + if (retval <= 0) { /* this is a fatal error */ + motion_log(LOG_ERR, 1, "%s: recv() fail after boundary string", + __FUNCTION__); + return -1; + } + + /* Reset the input buffer pointers */ + netcam->response->buffer_left = retval + netcam->response->buffer_left; + netcam->response->buffer_pos = netcam->response->buffer; + + /* This will cause a 'continue' of the main loop */ + bptr = NULL; + + /* Return to do the boundary compare from the start */ + break; + } /* end of while(1) input buffer search */ + + /* !bptr shows we're processing split boundary */ + if (!bptr) + continue; + } /* end of if (bptr) */ + + /* boundary string not present, so just write out as much data as possible */ + if (remaining) { + maxflush = MINVAL(netcam->response->buffer_left, remaining); + netcam_check_buffsize(buffer, maxflush); + retval = rbuf_flush(netcam, buffer->ptr + buffer->used, maxflush); + buffer->used += retval; + remaining -= retval; + } + } + + /* + * read is complete - set the current 'receiving' buffer atomically + * as 'latest', and make the buffer previously in 'latest' become + * the new 'receiving' + */ + if (gettimeofday(&curtime, NULL) < 0) + motion_log(LOG_ERR, 1, "%s: gettimeofday in netcam_read_jpeg", __FUNCTION__); + + + netcam->receiving->image_time = curtime; + + /* + * Calculate our "running average" time for this netcam's + * frame transmissions (except for the first time). + * Note that the average frame time is held in microseconds. + */ + if (netcam->last_image.tv_sec) { + netcam->av_frame_time = (9.0 * netcam->av_frame_time + + 1000000.0 * (curtime.tv_sec - netcam->last_image.tv_sec) + + (curtime.tv_usec- netcam->last_image.tv_usec)) / 10.0; + + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: Calculated frame time %f", + __FUNCTION__, netcam->av_frame_time); + } + netcam->last_image = curtime; + + pthread_mutex_lock(&netcam->mutex); + + xchg = netcam->latest; + netcam->latest = netcam->receiving; + netcam->receiving = xchg; + netcam->imgcnt++; + /* + * We have a new frame ready. We send a signal so that + * any thread (e.g. the motion main loop) waiting for the + * next frame to become available may proceed. + */ + pthread_cond_signal(&netcam->pic_ready); + + pthread_mutex_unlock(&netcam->mutex); + + if (netcam->caps.streaming == NCS_UNSUPPORTED) { + if (!netcam->connect_keepalive) { + if (debug_level > CAMERA_INFO) + motion_log(LOG_DEBUG, 0, "%s: netcam_read_html_jpeg disconnecting " + "netcam since keep-alive not set.", __FUNCTION__); + + netcam_disconnect(netcam); + } else if (debug_level > CAMERA_INFO) { + motion_log(LOG_DEBUG, 0, "%s: netcam_read_html_jpeg leaving netcam connected.", + __FUNCTION__); + } + } + + return 0; } /** @@ -1355,49 +1366,49 @@ static int netcam_read_html_jpeg(netcam_context_ptr netcam) */ static int netcam_http_request(netcam_context_ptr netcam) { - int ix; - - /* - * Our basic initialisation has been completed. Now we will attempt - * to connect with the camera so that we can then get a "header" - * in order to find out what kind of camera we are dealing with, - * as well as what are the picture dimensions. Note that for - * this initial connection, any failure will cause an error - * return from netcam_start (unlike later possible attempts at - * re-connecting, if the network connection is later interrupted). - */ - for (ix = 0; ix < MAX_HEADER_RETRIES; ix++) { - /* - * netcam_connect does an automatic netcam_close, so it's - * safe to include it as part of this loop - * (Not always true now Keep-Alive is implemented) - */ - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: about to try to connect, time #%d", - __FUNCTION__, ix ); - - if (netcam_connect(netcam, 0) != 0) { - motion_log(LOG_ERR, 0, "Failed to open camera - check your config " - "and that netcamera is online", __FUNCTION__); - - /* Fatal error on startup */ - ix = MAX_HEADER_RETRIES; - break;; - } - - if (netcam_read_first_header(netcam) >= 0) - break; - - motion_log(LOG_ERR, 0, "%s: Error reading first header - re-trying", __FUNCTION__); - } - - if (ix == MAX_HEADER_RETRIES) { - motion_log(LOG_ERR, 0, "%s: Failed to read first camera header " - "- giving up for now", __FUNCTION__); - return -1; - } - - return 0; + int ix; + + /* + * Our basic initialisation has been completed. Now we will attempt + * to connect with the camera so that we can then get a "header" + * in order to find out what kind of camera we are dealing with, + * as well as what are the picture dimensions. Note that for + * this initial connection, any failure will cause an error + * return from netcam_start (unlike later possible attempts at + * re-connecting, if the network connection is later interrupted). + */ + for (ix = 0; ix < MAX_HEADER_RETRIES; ix++) { + /* + * netcam_connect does an automatic netcam_close, so it's + * safe to include it as part of this loop + * (Not always true now Keep-Alive is implemented) + */ + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: about to try to connect, time #%d", + __FUNCTION__, ix); + + if (netcam_connect(netcam, 0) != 0) { + motion_log(LOG_ERR, 0, "Failed to open camera - check your config " + "and that netcamera is online", __FUNCTION__); + + /* Fatal error on startup */ + ix = MAX_HEADER_RETRIES; + break;; + } + + if (netcam_read_first_header(netcam) >= 0) + break; + + motion_log(LOG_ERR, 0, "%s: Error reading first header - re-trying", __FUNCTION__); + } + + if (ix == MAX_HEADER_RETRIES) { + motion_log(LOG_ERR, 0, "%s: Failed to read first camera header " + "- giving up for now", __FUNCTION__); + return -1; + } + + return 0; } /** @@ -1415,31 +1426,34 @@ static int netcam_http_request(netcam_context_ptr netcam) */ static int netcam_mjpg_buffer_refill(netcam_context_ptr netcam) { - int retval; - - if (netcam->response->buffer_left > 0) - return netcam->response->buffer_left; - while (1) { - retval = rbuf_read_bufferful(netcam); - if (retval <= 0) { /* If we got 0, we timeoutted. */ - motion_log(-1, 0, "%s: Read error, trying to reconnect..", __FUNCTION__); - /* We may have lost the connexion */ - if (netcam_http_request(netcam) < 0) { - motion_log(-1, 0, "%s: lost the cam.", __FUNCTION__); - return -1; /* We REALLY lost the cam... bail out for now. */ - } - } - if (retval > 0) - break; - } - - netcam->response->buffer_left = retval; - netcam->response->buffer_pos = netcam->response->buffer; - - motion_log(LOG_DEBUG, 0, "%s: Refilled buffer with [%d] bytes from the network.", - __FUNCTION__, retval); - - return retval; + int retval; + + if (netcam->response->buffer_left > 0) + return netcam->response->buffer_left; + + while (1) { + retval = rbuf_read_bufferful(netcam); + if (retval <= 0) { /* If we got 0, we timeoutted. */ + motion_log(-1, 0, "%s: Read error, trying to reconnect..", __FUNCTION__); + /* We may have lost the connexion */ + if (netcam_http_request(netcam) < 0) { + motion_log(-1, 0, "%s: lost the cam.", __FUNCTION__); + return -1; /* We REALLY lost the cam... bail out for now. */ + } + } + + if (retval > 0) + break; + } + + netcam->response->buffer_left = retval; + netcam->response->buffer_pos = netcam->response->buffer; + + if (SETUP) + motion_log(LOG_DEBUG, 0, "%s: Refilled buffer with [%d] bytes from the network.", + __FUNCTION__, retval); + + return retval; } /** @@ -1477,145 +1491,145 @@ static int netcam_mjpg_buffer_refill(netcam_context_ptr netcam) */ static int netcam_read_mjpg_jpeg(netcam_context_ptr netcam) { - netcam_buff_ptr buffer; - netcam_buff *xchg; - struct timeval curtime; - mjpg_header mh; - size_t read_bytes; - int retval; - - /* - * Initialisation - set our local pointers to the context - * information - */ - buffer = netcam->receiving; - /* Assure the target buffer is empty */ - buffer->used = 0; - - if (netcam_mjpg_buffer_refill(netcam) < 0) - return -1; - - /* Loop until we have a complete JPG. */ - while (1) { - read_bytes = 0; - while (read_bytes < sizeof(mh)) { - - /* Transfer what we have in buffer in the header structure */ - retval = rbuf_flush(netcam, ((char *)&mh) + read_bytes, sizeof(mh) - read_bytes); - - read_bytes += retval; - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: Read [%d/%d] header bytes.", - __FUNCTION__, read_bytes, sizeof(mh)); - - - /* If we don't have received a full header, refill our buffer. */ - if (read_bytes < sizeof(mh)) { - if (netcam_mjpg_buffer_refill(netcam) < 0) - return -1; - } - } - - /* Now check the validity of our header. */ - if (strncmp(mh.mh_magic, MJPG_MH_MAGIC, MJPG_MH_MAGIC_SIZE)) { - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: Invalid header received, reconnecting", - __FUNCTION__); - - - /* - * We shall reconnect to restart the stream, and get a chance - * to resync. - */ - if (netcam_http_request(netcam) < 0) - return -1; /* We lost the cam... bail out. */ - /* Even there, we need to resync. */ - buffer->used = 0; - continue ; - } - - /* Make room for the chunk. */ - netcam_check_buffsize(buffer, (int) mh.mh_chunksize); - - read_bytes = 0; - while (read_bytes < mh.mh_chunksize) { - retval = rbuf_flush(netcam, buffer->ptr + buffer->used + read_bytes, - mh.mh_chunksize - read_bytes); - read_bytes += retval; - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: Read [%d/%d] chunk bytes, [%d/%d] total", - __FUNCTION__, read_bytes, mh.mh_chunksize, - buffer->used + read_bytes, mh.mh_framesize); - - if (retval < (int)(mh.mh_chunksize - read_bytes)){ - /* motion_log(-1, 0, "Chunk incomplete, going to refill."); */ - if (netcam_mjpg_buffer_refill(netcam) < 0) - return -1; - - } - } - buffer->used += read_bytes; - - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: Chunk complete, buffer used [%d] bytes.", - __FUNCTION__, buffer->used); - - - /* Is our JPG image complete ? */ - if (mh.mh_framesize == buffer->used) { - - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: Image complete, buffer used [%d] bytes.", - __FUNCTION__, buffer->used); - - break; - } - /* motion_log(LOG_ERR, 0, " -> Rlen now at [%d] bytes", rlen); */ - } - - /* - * read is complete - set the current 'receiving' buffer atomically - * as 'latest', and make the buffer previously in 'latest' become - * the new 'receiving' - */ - if (gettimeofday(&curtime, NULL) < 0) - motion_log(LOG_ERR, 1, "%s: gettimeofday in netcam_read_jpeg", __FUNCTION__); - - - netcam->receiving->image_time = curtime; - - /* - * Calculate our "running average" time for this netcam's - * frame transmissions (except for the first time). - * Note that the average frame time is held in microseconds. - */ - if (netcam->last_image.tv_sec) { - netcam->av_frame_time = (9.0 * netcam->av_frame_time + - 1000000.0 * (curtime.tv_sec - netcam->last_image.tv_sec) + - (curtime.tv_usec- netcam->last_image.tv_usec)) / 10.0; - - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: Calculated frame time %f", - __FUNCTION__, netcam->av_frame_time); - } - netcam->last_image = curtime; - - pthread_mutex_lock(&netcam->mutex); - - xchg = netcam->latest; - netcam->latest = netcam->receiving; - netcam->receiving = xchg; - netcam->imgcnt++; - /* - * We have a new frame ready. We send a signal so that - * any thread (e.g. the motion main loop) waiting for the - * next frame to become available may proceed. - */ - pthread_cond_signal(&netcam->pic_ready); - - pthread_mutex_unlock(&netcam->mutex); - - return 0; + netcam_buff_ptr buffer; + netcam_buff *xchg; + struct timeval curtime; + mjpg_header mh; + size_t read_bytes; + int retval; + + /* + * Initialisation - set our local pointers to the context + * information + */ + buffer = netcam->receiving; + /* Assure the target buffer is empty */ + buffer->used = 0; + + if (netcam_mjpg_buffer_refill(netcam) < 0) + return -1; + + /* Loop until we have a complete JPG. */ + while (1) { + read_bytes = 0; + while (read_bytes < sizeof(mh)) { + + /* Transfer what we have in buffer in the header structure */ + retval = rbuf_flush(netcam, ((char *)&mh) + read_bytes, sizeof(mh) - read_bytes); + + read_bytes += retval; + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: Read [%d/%d] header bytes.", + __FUNCTION__, read_bytes, sizeof(mh)); + + + /* If we don't have received a full header, refill our buffer. */ + if (read_bytes < sizeof(mh)) { + if (netcam_mjpg_buffer_refill(netcam) < 0) + return -1; + } + } + + /* Now check the validity of our header. */ + if (strncmp(mh.mh_magic, MJPG_MH_MAGIC, MJPG_MH_MAGIC_SIZE)) { + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: Invalid header received, reconnecting", + __FUNCTION__); + + + /* + * We shall reconnect to restart the stream, and get a chance + * to resync. + */ + if (netcam_http_request(netcam) < 0) + return -1; /* We lost the cam... bail out. */ + /* Even there, we need to resync. */ + buffer->used = 0; + continue ; + } + + /* Make room for the chunk. */ + netcam_check_buffsize(buffer, (int) mh.mh_chunksize); + + read_bytes = 0; + while (read_bytes < mh.mh_chunksize) { + retval = rbuf_flush(netcam, buffer->ptr + buffer->used + read_bytes, + mh.mh_chunksize - read_bytes); + read_bytes += retval; + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: Read [%d/%d] chunk bytes, [%d/%d] total", + __FUNCTION__, read_bytes, mh.mh_chunksize, + buffer->used + read_bytes, mh.mh_framesize); + + if (retval < (int)(mh.mh_chunksize - read_bytes)) { + /* motion_log(-1, 0, "Chunk incomplete, going to refill."); */ + if (netcam_mjpg_buffer_refill(netcam) < 0) + return -1; + + } + } + buffer->used += read_bytes; + + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: Chunk complete, buffer used [%d] bytes.", + __FUNCTION__, buffer->used); + + + /* Is our JPG image complete ? */ + if (mh.mh_framesize == buffer->used) { + + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: Image complete, buffer used [%d] bytes.", + __FUNCTION__, buffer->used); + + break; + } + /* motion_log(LOG_ERR, 0, " -> Rlen now at [%d] bytes", rlen); */ + } + + /* + * read is complete - set the current 'receiving' buffer atomically + * as 'latest', and make the buffer previously in 'latest' become + * the new 'receiving' + */ + if (gettimeofday(&curtime, NULL) < 0) + motion_log(LOG_ERR, 1, "%s: gettimeofday in netcam_read_jpeg", __FUNCTION__); + + + netcam->receiving->image_time = curtime; + + /* + * Calculate our "running average" time for this netcam's + * frame transmissions (except for the first time). + * Note that the average frame time is held in microseconds. + */ + if (netcam->last_image.tv_sec) { + netcam->av_frame_time = (9.0 * netcam->av_frame_time + + 1000000.0 * (curtime.tv_sec - netcam->last_image.tv_sec) + + (curtime.tv_usec- netcam->last_image.tv_usec)) / 10.0; + + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: Calculated frame time %f", + __FUNCTION__, netcam->av_frame_time); + } + netcam->last_image = curtime; + + pthread_mutex_lock(&netcam->mutex); + + xchg = netcam->latest; + netcam->latest = netcam->receiving; + netcam->receiving = xchg; + netcam->imgcnt++; + /* + * We have a new frame ready. We send a signal so that + * any thread (e.g. the motion main loop) waiting for the + * next frame to become available may proceed. + */ + pthread_cond_signal(&netcam->pic_ready); + + pthread_mutex_unlock(&netcam->mutex); + + return 0; } /** @@ -1628,80 +1642,78 @@ static int netcam_read_mjpg_jpeg(netcam_context_ptr netcam) */ static int netcam_read_ftp_jpeg(netcam_context_ptr netcam) { - netcam_buff_ptr buffer; - int len; - netcam_buff *xchg; - struct timeval curtime; - - /* Point to our working buffer */ - buffer = netcam->receiving; - buffer->used = 0; - - /* Request the image from the remote server */ - if (ftp_get_socket(netcam->ftp) <= 0) { - motion_log(LOG_ERR, 0, "%s: ftp_get_socket failed in netcam_read_jpeg", - __FUNCTION__); - return -1; - } - - /* Now fetch the image using ftp_read. Note this is a blocking call */ - do { - /* Assure there's enough room in the buffer */ - netcam_check_buffsize(buffer, FTP_BUF_SIZE); - - /* Do the read */ - if ((len = ftp_read(netcam->ftp, buffer->ptr + buffer->used, FTP_BUF_SIZE)) < 0) - return -1; - - buffer->used += len; - } while (len > 0); - - if (gettimeofday(&curtime, NULL) < 0) - motion_log(LOG_ERR, 1, "%s: gettimeofday in netcam_read_jpeg", __FUNCTION__); - - - netcam->receiving->image_time = curtime; - /* - * Calculate our "running average" time for this netcam's - * frame transmissions (except for the first time). - * Note that the average frame time is held in microseconds. - */ - if (netcam->last_image.tv_sec) { - netcam->av_frame_time = - ((9.0 * netcam->av_frame_time) + 1000000.0 * - (curtime.tv_sec - netcam->last_image.tv_sec) + - (curtime.tv_usec- netcam->last_image.tv_usec)) - / 10.0; - - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: Calculated frame time %f", - __FUNCTION__, netcam->av_frame_time); - } - - netcam->last_image = curtime; - - /* - * read is complete - set the current 'receiving' buffer atomically - * as 'latest', and make the buffer previously in 'latest' become - * the new 'receiving' - */ - pthread_mutex_lock(&netcam->mutex); - - xchg = netcam->latest; - netcam->latest = netcam->receiving; - netcam->receiving = xchg; - netcam->imgcnt++; - - /* - * We have a new frame ready. We send a signal so that - * any thread (e.g. the motion main loop) waiting for the - * next frame to become available may proceed. - */ - pthread_cond_signal(&netcam->pic_ready); - - pthread_mutex_unlock(&netcam->mutex); - - return 0; + netcam_buff_ptr buffer; + int len; + netcam_buff *xchg; + struct timeval curtime; + + /* Point to our working buffer */ + buffer = netcam->receiving; + buffer->used = 0; + + /* Request the image from the remote server */ + if (ftp_get_socket(netcam->ftp) <= 0) { + motion_log(LOG_ERR, 0, "%s: ftp_get_socket failed in netcam_read_jpeg", + __FUNCTION__); + return -1; + } + + /* Now fetch the image using ftp_read. Note this is a blocking call */ + do { + /* Assure there's enough room in the buffer */ + netcam_check_buffsize(buffer, FTP_BUF_SIZE); + + /* Do the read */ + if ((len = ftp_read(netcam->ftp, buffer->ptr + buffer->used, FTP_BUF_SIZE)) < 0) + return -1; + + buffer->used += len; + } while (len > 0); + + if (gettimeofday(&curtime, NULL) < 0) + motion_log(LOG_ERR, 1, "%s: gettimeofday in netcam_read_jpeg", __FUNCTION__); + + + netcam->receiving->image_time = curtime; + /* + * Calculate our "running average" time for this netcam's + * frame transmissions (except for the first time). + * Note that the average frame time is held in microseconds. + */ + if (netcam->last_image.tv_sec) { + netcam->av_frame_time = ((9.0 * netcam->av_frame_time) + 1000000.0 * + (curtime.tv_sec - netcam->last_image.tv_sec) + + (curtime.tv_usec- netcam->last_image.tv_usec)) / 10.0; + + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: Calculated frame time %f", + __FUNCTION__, netcam->av_frame_time); + } + + netcam->last_image = curtime; + + /* + * read is complete - set the current 'receiving' buffer atomically + * as 'latest', and make the buffer previously in 'latest' become + * the new 'receiving' + */ + pthread_mutex_lock(&netcam->mutex); + + xchg = netcam->latest; + netcam->latest = netcam->receiving; + netcam->receiving = xchg; + netcam->imgcnt++; + + /* + * We have a new frame ready. We send a signal so that + * any thread (e.g. the motion main loop) waiting for the + * next frame to become available may proceed. + */ + pthread_cond_signal(&netcam->pic_ready); + + pthread_mutex_unlock(&netcam->mutex); + + return 0; } @@ -1715,178 +1727,178 @@ static int netcam_read_ftp_jpeg(netcam_context_ptr netcam) */ static int netcam_read_file_jpeg(netcam_context_ptr netcam) { - int loop_counter = 0; - if (debug_level > CAMERA_VERBOSE) { - motion_log(-1, 0, "%s: Begin", __FUNCTION__); - } - netcam_buff_ptr buffer; - int len; - netcam_buff *xchg; - struct timeval curtime; - struct stat statbuf; - - /* Point to our working buffer */ - buffer = netcam->receiving; - buffer->used = 0; - - /*int fstat(int filedes, struct stat *buf);*/ - do { - - if( stat( netcam->file->path, &statbuf) ) { - motion_log(-1, 0, "%s: stat(%s) error", __FUNCTION__, netcam->file->path ); - return -1; - } - - if (debug_level > CAMERA_VERBOSE) - motion_log(-1, 0, "%s: statbuf.st_mtime[%d] != last_st_mtime[%d]", - __FUNCTION__, statbuf.st_mtime, netcam->file->last_st_mtime); - - - if( loop_counter>((POLLING_TIMEOUT*1000*1000)/(POLLING_TIME/1000)) ) { //its waits POLLING_TIMEOUT - motion_log(-1, 0, "%s: waiting new file image timeout", __FUNCTION__); - return -1; - } - - if (debug_level > CAMERA_VERBOSE) - motion_log(-1, 0, "%s: delay waiting new file image ", __FUNCTION__); - - - //SLEEP(netcam->timeout.tv_sec, netcam->timeout.tv_usec*1000 ); //its waits 5seconds - READ_TIMEOUT - SLEEP( 0, POLLING_TIME ); // its waits 500ms - /*return -1;*/ - loop_counter++; - - } while(statbuf.st_mtime == netcam->file->last_st_mtime); - - netcam->file->last_st_mtime = statbuf.st_mtime; - - if (debug_level > CAMERA_INFO) - motion_log(LOG_INFO, 0, "%s: processing new file image - st_mtime " - "%d", __FUNCTION__, netcam->file->last_st_mtime ); - - /* Assure there's enough room in the buffer */ - while( buffer->size < (size_t)statbuf.st_size ) - netcam_check_buffsize(buffer, statbuf.st_size ); - - - /* Do the read */ - netcam->file->control_file_desc = open( netcam->file->path, O_RDONLY ); - if( netcam->file->control_file_desc < 0 ) { - motion_log(-1, 0, "%s: open(%s) error:%d", __FUNCTION__, - netcam->file->path, netcam->file->control_file_desc ); - return -1; - } - - if ((len = read(netcam->file->control_file_desc, buffer->ptr + buffer->used, statbuf.st_size)) < 0) { - motion_log(-1, 0, "%s: read(%s) error:%d", __FUNCTION__, - netcam->file->control_file_desc, len ); - return -1; - } - - buffer->used += len; - - close( netcam->file->control_file_desc ); - - if (gettimeofday(&curtime, NULL) < 0) - motion_log(LOG_ERR, 1, "%s: gettimeofday in netcam_read_jpeg", __FUNCTION__); - - - netcam->receiving->image_time = curtime; - /* - * Calculate our "running average" time for this netcam's - * frame transmissions (except for the first time). - * Note that the average frame time is held in microseconds. - */ - if (netcam->last_image.tv_sec) { - netcam->av_frame_time = - ((9.0 * netcam->av_frame_time) + 1000000.0 * - (curtime.tv_sec - netcam->last_image.tv_sec) + - (curtime.tv_usec- netcam->last_image.tv_usec)) - / 10.0; - - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: Calculated frame time %f", __FUNCTION__, - netcam->av_frame_time); - } - - netcam->last_image = curtime; - - /* - * read is complete - set the current 'receiving' buffer atomically - * as 'latest', and make the buffer previously in 'latest' become - * the new 'receiving' - */ - pthread_mutex_lock(&netcam->mutex); - - xchg = netcam->latest; - netcam->latest = netcam->receiving; - netcam->receiving = xchg; - netcam->imgcnt++; - - /* - * We have a new frame ready. We send a signal so that - * any thread (e.g. the motion main loop) waiting for the - * next frame to become available may proceed. - */ - pthread_cond_signal(&netcam->pic_ready); - - pthread_mutex_unlock(&netcam->mutex); - - if (debug_level > CAMERA_VERBOSE) - motion_log(-1, 0, "%s: End", __FUNCTION__); - - return 0; + int loop_counter = 0; + + if (debug_level > CAMERA_VERBOSE) { + motion_log(-1, 0, "%s: Begin", __FUNCTION__); + } + + netcam_buff_ptr buffer; + int len; + netcam_buff *xchg; + struct timeval curtime; + struct stat statbuf; + + /* Point to our working buffer */ + buffer = netcam->receiving; + buffer->used = 0; + + /*int fstat(int filedes, struct stat *buf);*/ + do { + if (stat(netcam->file->path, &statbuf)) { + motion_log(-1, 0, "%s: stat(%s) error", __FUNCTION__, netcam->file->path); + return -1; + } + + if (debug_level > CAMERA_VERBOSE) + motion_log(-1, 0, "%s: statbuf.st_mtime[%d] != last_st_mtime[%d]", + __FUNCTION__, statbuf.st_mtime, netcam->file->last_st_mtime); + + + /* its waits POLLING_TIMEOUT */ + if (loop_counter>((POLLING_TIMEOUT*1000*1000)/(POLLING_TIME/1000))) { + motion_log(-1, 0, "%s: waiting new file image timeout", __FUNCTION__); + return -1; + } + + if (debug_level > CAMERA_VERBOSE) + motion_log(-1, 0, "%s: delay waiting new file image ", __FUNCTION__); + + //its waits 5seconds - READ_TIMEOUT + //SLEEP(netcam->timeout.tv_sec, netcam->timeout.tv_usec*1000); + SLEEP(0, POLLING_TIME); // its waits 500ms + /*return -1;*/ + loop_counter++; + + } while (statbuf.st_mtime == netcam->file->last_st_mtime); + + netcam->file->last_st_mtime = statbuf.st_mtime; + + if (debug_level > CAMERA_INFO) + motion_log(LOG_INFO, 0, "%s: processing new file image - st_mtime " + "%d", __FUNCTION__, netcam->file->last_st_mtime); + + /* Assure there's enough room in the buffer */ + while (buffer->size < (size_t)statbuf.st_size) + netcam_check_buffsize(buffer, statbuf.st_size); + + + /* Do the read */ + netcam->file->control_file_desc = open(netcam->file->path, O_RDONLY); + if (netcam->file->control_file_desc < 0) { + motion_log(-1, 0, "%s: open(%s) error: %d", __FUNCTION__, + netcam->file->path, netcam->file->control_file_desc); + return -1; + } + + if ((len = read(netcam->file->control_file_desc, buffer->ptr + buffer->used, statbuf.st_size)) < 0) { + motion_log(-1, 0, "%s: read(%s) error: %d", __FUNCTION__, + netcam->file->control_file_desc, len); + return -1; + } + + buffer->used += len; + + close(netcam->file->control_file_desc); + + if (gettimeofday(&curtime, NULL) < 0) + motion_log(LOG_ERR, 1, "%s: gettimeofday in netcam_read_jpeg", __FUNCTION__); + + + netcam->receiving->image_time = curtime; + /* + * Calculate our "running average" time for this netcam's + * frame transmissions (except for the first time). + * Note that the average frame time is held in microseconds. + */ + if (netcam->last_image.tv_sec) { + netcam->av_frame_time = ((9.0 * netcam->av_frame_time) + 1000000.0 * + (curtime.tv_sec - netcam->last_image.tv_sec) + + (curtime.tv_usec- netcam->last_image.tv_usec)) / 10.0; + + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: Calculated frame time %f", __FUNCTION__, + netcam->av_frame_time); + } + + netcam->last_image = curtime; + + /* + * read is complete - set the current 'receiving' buffer atomically + * as 'latest', and make the buffer previously in 'latest' become + * the new 'receiving' + */ + pthread_mutex_lock(&netcam->mutex); + + xchg = netcam->latest; + netcam->latest = netcam->receiving; + netcam->receiving = xchg; + netcam->imgcnt++; + + /* + * We have a new frame ready. We send a signal so that + * any thread (e.g. the motion main loop) waiting for the + * next frame to become available may proceed. + */ + pthread_cond_signal(&netcam->pic_ready); + + pthread_mutex_unlock(&netcam->mutex); + + if (debug_level > CAMERA_VERBOSE) + motion_log(-1, 0, "%s: End", __FUNCTION__); + + return 0; } tfile_context *file_new_context(void) { - tfile_context *ret; + tfile_context *ret; - /* note that mymalloc will exit on any problem */ - ret = mymalloc(sizeof(tfile_context)); - if (!ret) - return ret; + /* note that mymalloc will exit on any problem */ + ret = mymalloc(sizeof(tfile_context)); + if (!ret) + return ret; - memset(ret, 0, sizeof(tfile_context)); - return ret; + memset(ret, 0, sizeof(tfile_context)); + return ret; } void file_free_context(tfile_context* ctxt) { - if (ctxt == NULL) - return; + if (ctxt == NULL) + return; - if (ctxt->path != NULL) - free(ctxt->path); + if (ctxt->path != NULL) + free(ctxt->path); - free(ctxt); + free(ctxt); } static int netcam_setup_file(netcam_context_ptr netcam, struct url_t *url) { - if ((netcam->file = file_new_context()) == NULL) - return -1; + if ((netcam->file = file_new_context()) == NULL) + return -1; - /* - * We copy the strings out of the url structure into the ftp_context - * structure. By setting url->{string} to NULL we effectively "take - * ownership" of the string away from the URL (i.e. it won't be freed - * when we cleanup the url structure later). - */ - netcam->file->path = url->path; - url->path = NULL; + /* + * We copy the strings out of the url structure into the ftp_context + * structure. By setting url->{string} to NULL we effectively "take + * ownership" of the string away from the URL (i.e. it won't be freed + * when we cleanup the url structure later). + */ + netcam->file->path = url->path; + url->path = NULL; - if (debug_level > CAMERA_INFO) - motion_log(LOG_INFO, 0, "%s: netcam->file->path %s", - __FUNCTION__, netcam->file->path); + if (debug_level > CAMERA_INFO) + motion_log(LOG_INFO, 0, "%s: netcam->file->path %s", + __FUNCTION__, netcam->file->path); - netcam_url_free(url); + netcam_url_free(url); - netcam->get_image = netcam_read_file_jpeg; + netcam->get_image = netcam_read_file_jpeg; - return 0; + return 0; } /** @@ -1903,176 +1915,180 @@ static int netcam_setup_file(netcam_context_ptr netcam, struct url_t *url) */ static void *netcam_handler_loop(void *arg) { - int retval; - int open_error = 0; - netcam_context_ptr netcam = arg; - struct context *cnt = netcam->cnt; /* needed for the SETUP macro :-( */ - - /* Store the corresponding motion thread number in TLS also for this - * thread (necessary for 'motion_log' to function properly). - */ - pthread_setspecific(tls_key_threadnr, (void *)((unsigned long)cnt->threadnr)); - - if (SETUP) - motion_log(LOG_INFO, 0, "%s: Camera handler thread [%d] started", - __FUNCTION__, netcam->threadnr); - - /* - * The logic of our loop is very simple. If this is a non- - * streaming camera, we re-establish connection with the camera - * and read the header record. If it's a streaming camera, we - * position to the next "boundary string" in the input stream. - * In either case, we then read the following JPEG image into the - * next available buffer, updating the "next" and "latest" indices - * in our netcam * structure. The loop continues until netcam->finish - * or cnt->finish is set. - */ - - while (!netcam->finish) { - if (netcam->response) { /* if html input */ - if (netcam->caps.streaming == NCS_UNSUPPORTED) { - /* Non-streaming ie. jpeg */ - if (!netcam->connect_keepalive || (netcam->connect_keepalive && netcam->keepalive_timeup)) { - /* If keepalive flag set but time up, time to close this socket */ - if (netcam->connect_keepalive && netcam->keepalive_timeup) { - motion_log(LOG_INFO, 0, "%s: Closing netcam socket as Keep-Alive" - "time is up (camera sent Close field). A reconnect should happen.", - __FUNCTION__); - netcam_disconnect(netcam); - netcam->keepalive_timeup = FALSE; - } - /* And the netcam_connect call below will open a new one */ - if (netcam_connect(netcam, open_error) < 0) { - if (!open_error) { /* log first error */ - motion_log(LOG_ERR, 0, - "%s: re-opening camera (non-streaming)", __FUNCTION__); - open_error = 1; - } - /* need to have a dynamic delay here */ - SLEEP(5, 0); - continue; - } - if (open_error) { /* log re-connection */ - motion_log(LOG_ERR, 0, - "%s: camera re-connected", __FUNCTION__); - open_error = 0; - } - } - /* Send our request and look at the response */ - if ((retval = netcam_read_first_header(netcam)) != 1) { - if (retval > 0) { - motion_log(LOG_ERR, 0, "%s: Unrecognized image header (%d)", - __FUNCTION__, retval); - } else if (retval != -1) { - motion_log(LOG_ERR, 0, "%s: Error in header (%d)", - __FUNCTION__, retval); - } - /* need to have a dynamic delay here */ - continue; - } - } else if (netcam->caps.streaming == NCS_MULTIPART) { /* Multipart Streaming */ - if (netcam_read_next_header(netcam) < 0) { - if (netcam_connect(netcam, open_error) < 0) { - if (!open_error) { /* log first error */ - motion_log(LOG_ERR, 0, - "%s: re-opening camera (streaming)", __FUNCTION__); - open_error = 1; - } - SLEEP(5, 0); - continue; - } - - if ((retval = netcam_read_first_header(netcam) != 2)) { - if (retval > 0) { - motion_log(LOG_ERR, 0, - "%s: Unrecognized image header (%d)", __FUNCTION__, - retval); - } else if (retval != -1) { - motion_log(LOG_ERR, 0, - "%s: Error in header (%d)", __FUNCTION__, retval); - } - /* FIXME need some limit */ - continue; - } - } - if (open_error) { /* log re-connection */ - motion_log(LOG_ERR, 0, - "%s: camera re-connected", __FUNCTION__); - open_error = 0; - } - } else if (netcam->caps.streaming == NCS_BLOCK) { /* MJPG-Block streaming */ - /* - * Since we cannot move in the stream here, because we will read past the - * MJPG-block-header, error handling is done while reading MJPG blocks. - */ - } - } - if (netcam->get_image(netcam) < 0) { - motion_log(LOG_ERR, 0, "%s: Error getting jpeg image", __FUNCTION__); - /* if FTP connection, attempt to re-connect to server */ - if (netcam->ftp) { - close(netcam->ftp->control_file_desc); - if (ftp_connect(netcam) < 0) - motion_log(LOG_ERR, 0, "%s: Trying to re-connect", __FUNCTION__); - - } - continue; - } - /* - * FIXME - * Need to check whether the image was received / decoded - * satisfactorily - */ - - /* - * If non-streaming, want to synchronize our thread with the - * motion main-loop. - */ - if (netcam->caps.streaming == NCS_UNSUPPORTED) { - pthread_mutex_lock(&netcam->mutex); - - /* before anything else, check for system shutdown */ - if (netcam->finish) { - pthread_mutex_unlock(&netcam->mutex); - break; - } - - /* - * If our current loop has finished before the next - * request from the motion main-loop, we do a - * conditional wait (wait for signal). On the other - * hand, if the motion main-loop has already signalled - * us, we just continue. In either event, we clear - * the start_capture flag set by the main loop. - */ - if (!netcam->start_capture) - pthread_cond_wait(&netcam->cap_cond, &netcam->mutex); - - netcam->start_capture = 0; - - pthread_mutex_unlock(&netcam->mutex); - } - /* the loop continues forever, or until motion shutdown */ - } - - /* our thread is finished - decrement motion's thread count */ - pthread_mutex_lock(&global_lock); - threads_running--; - pthread_mutex_unlock(&global_lock); - - /* log out a termination message */ - motion_log(LOG_INFO, 0, "%s: netcam camera handler: finish set, exiting", __FUNCTION__); - - /* setting netcam->thread_id to zero shows netcam_cleanup we're done */ - netcam->thread_id = 0; - - /* signal netcam_cleanup that we're all done */ - pthread_mutex_lock(&netcam->mutex); - pthread_cond_signal(&netcam->exiting); - pthread_mutex_unlock(&netcam->mutex); - - /* Goodbye..... */ - pthread_exit(NULL); + int retval; + int open_error = 0; + netcam_context_ptr netcam = arg; + struct context *cnt = netcam->cnt; /* needed for the SETUP macro :-( */ + + /* Store the corresponding motion thread number in TLS also for this + * thread (necessary for 'motion_log' to function properly). + */ + pthread_setspecific(tls_key_threadnr, (void *)((unsigned long)cnt->threadnr)); + + if (SETUP) + motion_log(LOG_INFO, 0, "%s: Camera handler thread [%d] started", + __FUNCTION__, netcam->threadnr); + + /* + * The logic of our loop is very simple. If this is a non- + * streaming camera, we re-establish connection with the camera + * and read the header record. If it's a streaming camera, we + * position to the next "boundary string" in the input stream. + * In either case, we then read the following JPEG image into the + * next available buffer, updating the "next" and "latest" indices + * in our netcam * structure. The loop continues until netcam->finish + * or cnt->finish is set. + */ + + while (!netcam->finish) { + if (netcam->response) { /* if html input */ + if (netcam->caps.streaming == NCS_UNSUPPORTED) { + /* Non-streaming ie. jpeg */ + if (!netcam->connect_keepalive || (netcam->connect_keepalive && netcam->keepalive_timeup)) { + /* If keepalive flag set but time up, time to close this socket */ + if (netcam->connect_keepalive && netcam->keepalive_timeup) { + motion_log(LOG_INFO, 0, "%s: Closing netcam socket as Keep-Alive" + "time is up (camera sent Close field). A reconnect should happen.", + __FUNCTION__); + netcam_disconnect(netcam); + netcam->keepalive_timeup = FALSE; + } + + /* And the netcam_connect call below will open a new one */ + if (netcam_connect(netcam, open_error) < 0) { + if (!open_error) { /* log first error */ + motion_log(LOG_ERR, 0, + "%s: re-opening camera (non-streaming)", __FUNCTION__); + open_error = 1; + } + /* need to have a dynamic delay here */ + SLEEP(5, 0); + continue; + } + + if (open_error) { /* log re-connection */ + motion_log(LOG_ERR, 0, + "%s: camera re-connected", __FUNCTION__); + open_error = 0; + } + } + /* Send our request and look at the response */ + if ((retval = netcam_read_first_header(netcam)) != 1) { + if (retval > 0) { + motion_log(LOG_ERR, 0, "%s: Unrecognized image header (%d)", + __FUNCTION__, retval); + } else if (retval != -1) { + motion_log(LOG_ERR, 0, "%s: Error in header (%d)", + __FUNCTION__, retval); + } + /* need to have a dynamic delay here */ + continue; + } + } else if (netcam->caps.streaming == NCS_MULTIPART) { /* Multipart Streaming */ + if (netcam_read_next_header(netcam) < 0) { + if (netcam_connect(netcam, open_error) < 0) { + if (!open_error) { /* log first error */ + motion_log(LOG_ERR, 0, + "%s: re-opening camera (streaming)", + __FUNCTION__); + open_error = 1; + } + SLEEP(5, 0); + continue; + } + + if ((retval = netcam_read_first_header(netcam) != 2)) { + if (retval > 0) { + motion_log(LOG_ERR, 0, + "%s: Unrecognized image header (%d)", __FUNCTION__, + retval); + } else if (retval != -1) { + motion_log(LOG_ERR, 0, + "%s: Error in header (%d)", __FUNCTION__, retval); + } + /* FIXME need some limit */ + continue; + } + } + if (open_error) { /* log re-connection */ + motion_log(LOG_ERR, 0, + "%s: camera re-connected", __FUNCTION__); + open_error = 0; + } + } else if (netcam->caps.streaming == NCS_BLOCK) { /* MJPG-Block streaming */ + /* + * Since we cannot move in the stream here, because we will read past the + * MJPG-block-header, error handling is done while reading MJPG blocks. + */ + } + } + if (netcam->get_image(netcam) < 0) { + motion_log(LOG_ERR, 0, "%s: Error getting jpeg image", __FUNCTION__); + /* if FTP connection, attempt to re-connect to server */ + if (netcam->ftp) { + close(netcam->ftp->control_file_desc); + if (ftp_connect(netcam) < 0) + motion_log(LOG_ERR, 0, "%s: Trying to re-connect", __FUNCTION__); + + } + continue; + } + /* + * FIXME + * Need to check whether the image was received / decoded + * satisfactorily + */ + + /* + * If non-streaming, want to synchronize our thread with the + * motion main-loop. + */ + if (netcam->caps.streaming == NCS_UNSUPPORTED) { + pthread_mutex_lock(&netcam->mutex); + + /* before anything else, check for system shutdown */ + if (netcam->finish) { + pthread_mutex_unlock(&netcam->mutex); + break; + } + + /* + * If our current loop has finished before the next + * request from the motion main-loop, we do a + * conditional wait (wait for signal). On the other + * hand, if the motion main-loop has already signalled + * us, we just continue. In either event, we clear + * the start_capture flag set by the main loop. + */ + if (!netcam->start_capture) + pthread_cond_wait(&netcam->cap_cond, &netcam->mutex); + + netcam->start_capture = 0; + + pthread_mutex_unlock(&netcam->mutex); + } + /* the loop continues forever, or until motion shutdown */ + } + + /* our thread is finished - decrement motion's thread count */ + pthread_mutex_lock(&global_lock); + threads_running--; + pthread_mutex_unlock(&global_lock); + + /* log out a termination message */ + motion_log(LOG_INFO, 0, "%s: netcam camera handler: finish set, exiting", + __FUNCTION__); + + /* setting netcam->thread_id to zero shows netcam_cleanup we're done */ + netcam->thread_id = 0; + + /* signal netcam_cleanup that we're all done */ + pthread_mutex_lock(&netcam->mutex); + pthread_cond_signal(&netcam->exiting); + pthread_mutex_unlock(&netcam->mutex); + + /* Goodbye..... */ + pthread_exit(NULL); } /** @@ -2092,159 +2108,161 @@ static void *netcam_handler_loop(void *arg) */ static int netcam_http_build_url(netcam_context_ptr netcam, struct url_t *url) { - struct context *cnt = netcam->cnt; - const char *ptr; /* working var */ - char *userpass; /* temp pointer to config value */ - char *encuserpass; /* temp storage for encoded ver */ - char *request_pass = NULL; /* temp storage for base64 conv */ - int ix; - - /* First the http context structure */ - netcam->response = (struct rbuf *) mymalloc(sizeof(struct rbuf)); - memset(netcam->response, 0, sizeof(struct rbuf)); - - if (debug_level > CAMERA_INFO) - motion_log(LOG_INFO, 0, "%s: Netcam has flags: HTTP/1.0: %s HTTP/1.1: %s Keep-Alive %s.", - __FUNCTION__, netcam->connect_http_10 ? "1":"0", netcam->connect_http_11 ? "1":"0", - netcam->connect_keepalive ? "ON":"OFF"); - - /* - * The network camera may require a username and password. If - * so, the information can come from two different places in the - * motion configuration file. Either it can be present in - * the netcam_userpass, or it can be present as a part of the URL - * for the camera. We assume the first of these has a higher - * relevance. - */ - if (cnt->conf.netcam_userpass) - ptr = cnt->conf.netcam_userpass; - else - ptr = url->userpass; - - /* base64_encode needs up to 3 additional chars */ - if (ptr) { - userpass = mymalloc(strlen(ptr) + 3); - strcpy(userpass, ptr); - } else - userpass = NULL; - - /* - * Now we want to create the actual string which will be used to - * connect to the camera. It may or may not contain a username / - * password. We first compose a basic connect message, then check - * if a Keep-Alive header is to be included (or just 'close'), then - * whether a username / password is required and, if so, just - * concatenate it with the request. - * - */ - /* space for final \r\n plus string terminator */ - ix = 3; - - /* See if username / password is required */ - if (userpass) { /* if either of the above are non-NULL */ - /* Allocate space for the base64-encoded string */ - encuserpass = mymalloc(BASE64_LENGTH(strlen(userpass)) + 1); - /* Fill in the value */ - base64_encode(userpass, encuserpass, strlen(userpass)); - /* Now create the last part (authorization) of the request */ - request_pass = mymalloc(strlen(connect_auth_req) + - strlen(encuserpass) + 1); - ix += sprintf(request_pass, connect_auth_req, encuserpass); - /* free the working variables */ - free(encuserpass); - } - - /* - * We are now ready to set up the netcam's "connect request". Most of - * this comes from the (preset) string 'connect_req', but additional - * characters are required if there is a proxy server, or if there is - * a Keep-Alive connection rather than a close connection, or - * a username / password for the camera. The variable 'ix' currently - * has the number of characters required for username/password (which - * could be zero) and for the \r\n and string terminator. We will also - * always need space for the netcam path, and if a proxy is being used - * we also need space for a preceding 'http://{hostname}' for the - * netcam path. - * Note: Keep-Alive (but not HTTP 1.1) is disabled if a proxy URL - * is set, since HTTP 1.0 Keep-alive cannot be transferred through. - */ - if (cnt->conf.netcam_proxy) { - /* - * Allocate space for a working string to contain the path. - * The extra 4 is for "://" and string terminator. - */ - ptr = mymalloc(strlen(url->service) + strlen(url->host) - + strlen(url->path) + 4); - sprintf((char *)ptr, "http://%s%s", url->host, url->path); - netcam->connect_keepalive = 0; /* Disable Keepalive if proxy */ - if (debug_level > CAMERA_INFO) - motion_log(LOG_DEBUG, 0, "%s: Removed netcam_keepalive flag due to proxy set." - "Proxy is incompatible with Keep-Alive.", __FUNCTION__); - } else { - /* if no proxy, set as netcam_url path */ - ptr = url->path; - /* - * after generating the connect message the string - * will be freed, so we don't want netcam_url_free - * to free it as well. - */ - url->path = NULL; - } - - ix += strlen(ptr); - - /* Now add the required number of characters for the close header - * or Keep-Alive header. We test the flag which can be unset if - * there is a problem (rather than the flag in the conf structure - * which is read-only. - */ + struct context *cnt = netcam->cnt; + const char *ptr; /* working var */ + char *userpass; /* temp pointer to config value */ + char *encuserpass; /* temp storage for encoded ver */ + char *request_pass = NULL; /* temp storage for base64 conv */ + int ix; + + /* First the http context structure */ + netcam->response = (struct rbuf *) mymalloc(sizeof(struct rbuf)); + memset(netcam->response, 0, sizeof(struct rbuf)); + + if (debug_level > CAMERA_INFO) + motion_log(LOG_INFO, 0, "%s: Netcam has flags: HTTP/1.0: %s HTTP/1.1: %s Keep-Alive %s.", + __FUNCTION__, netcam->connect_http_10 ? "1":"0", netcam->connect_http_11 ? "1":"0", + netcam->connect_keepalive ? "ON":"OFF"); + + /* + * The network camera may require a username and password. If + * so, the information can come from two different places in the + * motion configuration file. Either it can be present in + * the netcam_userpass, or it can be present as a part of the URL + * for the camera. We assume the first of these has a higher + * relevance. + */ + if (cnt->conf.netcam_userpass) + ptr = cnt->conf.netcam_userpass; + else + ptr = url->userpass; + + /* base64_encode needs up to 3 additional chars */ + if (ptr) { + userpass = mymalloc(strlen(ptr) + 3); + strcpy(userpass, ptr); + } else { + userpass = NULL; + } + + /* + * Now we want to create the actual string which will be used to + * connect to the camera. It may or may not contain a username / + * password. We first compose a basic connect message, then check + * if a Keep-Alive header is to be included (or just 'close'), then + * whether a username / password is required and, if so, just + * concatenate it with the request. + * + */ + /* space for final \r\n plus string terminator */ + ix = 3; + + /* See if username / password is required */ + if (userpass) { /* if either of the above are non-NULL */ + /* Allocate space for the base64-encoded string */ + encuserpass = mymalloc(BASE64_LENGTH(strlen(userpass)) + 1); + /* Fill in the value */ + base64_encode(userpass, encuserpass, strlen(userpass)); + /* Now create the last part (authorization) of the request */ + request_pass = mymalloc(strlen(connect_auth_req) + + strlen(encuserpass) + 1); + ix += sprintf(request_pass, connect_auth_req, encuserpass); + /* free the working variables */ + free(encuserpass); + } + + /* + * We are now ready to set up the netcam's "connect request". Most of + * this comes from the (preset) string 'connect_req', but additional + * characters are required if there is a proxy server, or if there is + * a Keep-Alive connection rather than a close connection, or + * a username / password for the camera. The variable 'ix' currently + * has the number of characters required for username/password (which + * could be zero) and for the \r\n and string terminator. We will also + * always need space for the netcam path, and if a proxy is being used + * we also need space for a preceding 'http://{hostname}' for the + * netcam path. + * Note: Keep-Alive (but not HTTP 1.1) is disabled if a proxy URL + * is set, since HTTP 1.0 Keep-alive cannot be transferred through. + */ + if (cnt->conf.netcam_proxy) { + /* + * Allocate space for a working string to contain the path. + * The extra 4 is for "://" and string terminator. + */ + ptr = mymalloc(strlen(url->service) + strlen(url->host) + + strlen(url->path) + 4); + sprintf((char *)ptr, "http://%s%s", url->host, url->path); + netcam->connect_keepalive = 0; /* Disable Keepalive if proxy */ + if (debug_level > CAMERA_INFO) + motion_log(LOG_DEBUG, 0, "%s: Removed netcam_keepalive flag due to proxy set." + "Proxy is incompatible with Keep-Alive.", __FUNCTION__); + } else { + /* if no proxy, set as netcam_url path */ + ptr = url->path; + /* + * after generating the connect message the string + * will be freed, so we don't want netcam_url_free + * to free it as well. + */ + url->path = NULL; + } + + ix += strlen(ptr); + + /* Now add the required number of characters for the close header + * or Keep-Alive header. We test the flag which can be unset if + * there is a problem (rather than the flag in the conf structure + * which is read-only. + */ - if (netcam->connect_keepalive) { - ix += strlen(connect_req_keepalive); - } else { - ix += strlen(connect_req_close); - } - - /* Point to either the HTTP 1.0 or 1.1 request header set */ - /* If the configuration is anything other than 1.1, use 1.0 */ - /* as a default. This avoids a chance of being left with none */ - if (netcam->connect_http_11==TRUE) - connect_req = connect_req_http11; - else - connect_req = connect_req_http10; - - /* - * Now that we know how much space we need, we can allocate space - * for the connect-request string. - */ - netcam->connect_request = mymalloc(strlen(connect_req) + ix + - strlen(netcam->connect_host)); - - /* Now create the request string with an sprintf */ - sprintf(netcam->connect_request, connect_req, ptr, - netcam->connect_host); - - if (netcam->connect_keepalive) { - strcat(netcam->connect_request, connect_req_keepalive); - } else { - strcat(netcam->connect_request, connect_req_close); - } - - if (userpass) { - strcat(netcam->connect_request, request_pass); - free(request_pass); - free(userpass); - } - - /* put on the final CRLF onto the request */ - strcat(netcam->connect_request, "\r\n"); - free((void *)ptr); - netcam_url_free(url); /* Cleanup the url data */ - - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: Camera connect string is ''%s'' End of camera connect string.", - __FUNCTION__, netcam->connect_request); - return 0; + if (netcam->connect_keepalive) + ix += strlen(connect_req_keepalive); + else + ix += strlen(connect_req_close); + + + /* Point to either the HTTP 1.0 or 1.1 request header set + * If the configuration is anything other than 1.1, use 1.0 + * as a default. This avoids a chance of being left with none + */ + if (netcam->connect_http_11 == TRUE) + connect_req = connect_req_http11; + else + connect_req = connect_req_http10; + + /* + * Now that we know how much space we need, we can allocate space + * for the connect-request string. + */ + netcam->connect_request = mymalloc(strlen(connect_req) + ix + + strlen(netcam->connect_host)); + + /* Now create the request string with an sprintf */ + sprintf(netcam->connect_request, connect_req, ptr, + netcam->connect_host); + + if (netcam->connect_keepalive) + strcat(netcam->connect_request, connect_req_keepalive); + else + strcat(netcam->connect_request, connect_req_close); + + + if (userpass) { + strcat(netcam->connect_request, request_pass); + free(request_pass); + free(userpass); + } + + /* put on the final CRLF onto the request */ + strcat(netcam->connect_request, "\r\n"); + free((void *)ptr); + netcam_url_free(url); /* Cleanup the url data */ + + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: Camera connect string is ''%s'' End of camera connect string.", + __FUNCTION__, netcam->connect_request); + return 0; } /** @@ -2264,36 +2282,36 @@ static int netcam_http_build_url(netcam_context_ptr netcam, struct url_t *url) */ static int netcam_setup_html(netcam_context_ptr netcam, struct url_t *url) { - /* - * This netcam is http-based, so build the required URL and - * structures, like the connection-string and so on. - */ - if (netcam_http_build_url(netcam, url) < 0) - return -1; - - /* - * Then we will send our http request and get headers. - */ - if (netcam_http_request(netcam) < 0) - return -1; - - /* - * If this is a streaming camera, we need to position just - * past the boundary string and read the image header - */ - if (netcam->caps.streaming == NCS_MULTIPART) { - if (netcam_read_next_header(netcam) < 0) { - motion_log(LOG_ERR, 0, "%s: Failed to read first stream header - " - "giving up for now", __FUNCTION__); - return -1; - } - } - - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: connected, going on to read image.", __FUNCTION__); - - netcam->get_image = netcam_read_html_jpeg; - return 0; + /* + * This netcam is http-based, so build the required URL and + * structures, like the connection-string and so on. + */ + if (netcam_http_build_url(netcam, url) < 0) + return -1; + + /* + * Then we will send our http request and get headers. + */ + if (netcam_http_request(netcam) < 0) + return -1; + + /* + * If this is a streaming camera, we need to position just + * past the boundary string and read the image header + */ + if (netcam->caps.streaming == NCS_MULTIPART) { + if (netcam_read_next_header(netcam) < 0) { + motion_log(LOG_ERR, 0, "%s: Failed to read first stream header - " + "giving up for now", __FUNCTION__); + return -1; + } + } + + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: connected, going on to read image.", __FUNCTION__); + + netcam->get_image = netcam_read_html_jpeg; + return 0; } /** @@ -2311,88 +2329,89 @@ static int netcam_setup_html(netcam_context_ptr netcam, struct url_t *url) */ static int netcam_setup_mjpg(netcam_context_ptr netcam, struct url_t *url) { - /* - * This netcam is http-based, so build the required URL and - * structures, like the connection-string and so on. - */ - if (netcam_http_build_url(netcam, url) != 0) - return -1; - - /* - * Then we will send our http request and get headers. - */ - if (netcam_http_request(netcam) < 0) - return -1; - - /* - * We have a special type of streaming camera - */ - netcam->caps.streaming = NCS_BLOCK; - - /* - * We are positionned right just at the start of the first MJPG - * header, so don't move anymore, initialization complete. - */ - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: connected, going on to read and decode MJPG chunks.", __FUNCTION__); - - netcam->get_image = netcam_read_mjpg_jpeg; - - return 0; + /* + * This netcam is http-based, so build the required URL and + * structures, like the connection-string and so on. + */ + if (netcam_http_build_url(netcam, url) != 0) + return -1; + + /* + * Then we will send our http request and get headers. + */ + if (netcam_http_request(netcam) < 0) + return -1; + + /* + * We have a special type of streaming camera + */ + netcam->caps.streaming = NCS_BLOCK; + + /* + * We are positionned right just at the start of the first MJPG + * header, so don't move anymore, initialization complete. + */ + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: connected, going on to read and decode MJPG chunks.", + __FUNCTION__); + + netcam->get_image = netcam_read_mjpg_jpeg; + + return 0; } static int netcam_setup_ftp(netcam_context_ptr netcam, struct url_t *url) { - struct context *cnt = netcam->cnt; - const char *ptr; - - if ((netcam->ftp = ftp_new_context()) == NULL) - return -1; - /* - * We copy the strings out of the url structure into the ftp_context - * structure. By setting url->{string} to NULL we effectively "take - * ownership" of the string away from the URL (i.e. it won't be freed - * when we cleanup the url structure later). - */ - netcam->ftp->path = url->path; - url->path = NULL; - - if (cnt->conf.netcam_userpass != NULL) - ptr = cnt->conf.netcam_userpass; - else { - ptr = url->userpass; /* don't set this one NULL, gets freed */ - } - - if (ptr != NULL) { - char *cptr; - - if ((cptr = strchr(ptr, ':')) == NULL) - netcam->ftp->user = mystrdup(ptr); - else { - netcam->ftp->user = mymalloc((cptr - ptr)); - memcpy(netcam->ftp->user, ptr,(cptr - ptr)); - netcam->ftp->passwd = mystrdup(cptr + 1); - } - } - - netcam_url_free(url); - - /* - * The ftp context should be all ready to attempt a connection with - * the server, so we try .... - */ - if (ftp_connect(netcam) < 0) { - ftp_free_context(netcam->ftp); - return -1; - } - - if (ftp_send_type(netcam->ftp, 'I') < 0) { - motion_log(LOG_ERR, 0, "%s: Error sending TYPE I to ftp server", __FUNCTION__); - return -1; - } - - netcam->get_image = netcam_read_ftp_jpeg; - return 0; + struct context *cnt = netcam->cnt; + const char *ptr; + + if ((netcam->ftp = ftp_new_context()) == NULL) + return -1; + /* + * We copy the strings out of the url structure into the ftp_context + * structure. By setting url->{string} to NULL we effectively "take + * ownership" of the string away from the URL (i.e. it won't be freed + * when we cleanup the url structure later). + */ + netcam->ftp->path = url->path; + url->path = NULL; + + if (cnt->conf.netcam_userpass != NULL) + ptr = cnt->conf.netcam_userpass; + else { + ptr = url->userpass; /* don't set this one NULL, gets freed */ + } + + if (ptr != NULL) { + char *cptr; + + if ((cptr = strchr(ptr, ':')) == NULL) { + netcam->ftp->user = mystrdup(ptr); + } else { + netcam->ftp->user = mymalloc((cptr - ptr)); + memcpy(netcam->ftp->user, ptr,(cptr - ptr)); + netcam->ftp->passwd = mystrdup(cptr + 1); + } + } + + netcam_url_free(url); + + /* + * The ftp context should be all ready to attempt a connection with + * the server, so we try .... + */ + if (ftp_connect(netcam) < 0) { + ftp_free_context(netcam->ftp); + return -1; + } + + if (ftp_send_type(netcam->ftp, 'I') < 0) { + motion_log(LOG_ERR, 0, "%s: Error sending TYPE I to ftp server", __FUNCTION__); + return -1; + } + + netcam->get_image = netcam_read_ftp_jpeg; + return 0; } /** @@ -2415,23 +2434,22 @@ static int netcam_setup_ftp(netcam_context_ptr netcam, struct url_t *url) */ ssize_t netcam_recv(netcam_context_ptr netcam, void *buffptr, size_t buffsize) { - ssize_t retval; - fd_set fd_r; - struct timeval selecttime; + ssize_t retval; + fd_set fd_r; + struct timeval selecttime; - if (netcam->sock < 0) - return -1; /* We are not connected, it's impossible to receive data. */ + if (netcam->sock < 0) + return -1; /* We are not connected, it's impossible to receive data. */ - FD_ZERO(&fd_r); - FD_SET(netcam->sock, &fd_r); - selecttime = netcam->timeout; + FD_ZERO(&fd_r); + FD_SET(netcam->sock, &fd_r); + selecttime = netcam->timeout; - retval = select(FD_SETSIZE, &fd_r, NULL, NULL, &selecttime); - if (retval == 0) { /* 0 means timeout */ - return -1; - } + retval = select(FD_SETSIZE, &fd_r, NULL, NULL, &selecttime); + if (retval == 0) /* 0 means timeout */ + return -1; - return recv(netcam->sock, buffptr, buffsize, 0); + return recv(netcam->sock, buffptr, buffsize, 0); } /** @@ -2458,133 +2476,130 @@ ssize_t netcam_recv(netcam_context_ptr netcam, void *buffptr, size_t buffsize) */ void netcam_cleanup(netcam_context_ptr netcam, int init_retry_flag) { - struct timespec waittime; - - if (!netcam) - return; - - /* - * This 'lock' is just a bit of "defensive" programming. It should - * only be necessary if the routine is being called from different - * threads, but in our Motion design, it should only be called from - * the motion main-loop. - */ - pthread_mutex_lock(&netcam->mutex); - - if (netcam->cnt->netcam == NULL) { - return; - } - - /* - * We set the netcam_context pointer in the motion main-loop context - * to be NULL, so that this routine won't be called a second time - */ - netcam->cnt->netcam = NULL; - - /* - * Next we set 'finish' in order to get the camera-handler thread - * to stop. - */ - netcam->finish = 1; - - /* - * If the camera is non-streaming, the handler thread could be waiting - * for a signal, so we send it one. If it's actually waiting on the - * condition, it won't actually start yet because we still have - * netcam->mutex locked. - */ - - if (netcam->caps.streaming == NCS_UNSUPPORTED) { - pthread_cond_signal(&netcam->cap_cond); - } - - /* - * Once the camera-handler gets to the end of it's loop (probably as - * soon as we release netcam->mutex), because netcam->finish has been - * set it will exit it's loop, do anything it needs to do with the - * netcam context, and then send *us* as signal (netcam->exiting). - * Note that when we start our wait on netcam->exiting, our lock on - * netcam->mutex is automatically released, which will allow the - * handler to complete it's loop, notice that 'finish' is set and exit. - * This should always work, but again (defensive programming) we - * use pthread_cond_timedwait and, if our timeout (8 seconds) expires - * we just do the cleanup the handler would normally have done. This - * assures that (even if there is a bug in our code) motion will still - * be able to exit. - * If the init_retry_flag is not set the netcam_cleanup code was - * called while retrying the initial connection to a netcam and then - * there is no camera-handler started yet and thread_running must - * not be decremented. - */ - waittime.tv_sec = time(NULL) + 8; /* Seems that 3 is too small */ - waittime.tv_nsec = 0; - - if (!init_retry_flag && - pthread_cond_timedwait(&netcam->exiting, &netcam->mutex, &waittime) != 0) { - /* - * Although this shouldn't happen, if it *does* happen we will - * log it (just for the programmer's information) - */ - motion_log(-1, 0, "%s: No response from camera " - "handler - it must have already died", - __FUNCTION__); - pthread_mutex_lock(&global_lock); - threads_running--; - pthread_mutex_unlock(&global_lock); - } - - /* we don't need any lock anymore, so release it */ - pthread_mutex_unlock(&netcam->mutex); - - /* and cleanup the rest of the netcam_context structure */ - if (netcam->connect_host != NULL) { - free(netcam->connect_host); - } - - if (netcam->connect_request != NULL) { - free(netcam->connect_request); - } - - if (netcam->boundary != NULL) { - free(netcam->boundary); - } - - if (netcam->latest != NULL) { - if (netcam->latest->ptr != NULL) { - free(netcam->latest->ptr); - } - free(netcam->latest); - } - - if (netcam->receiving != NULL) { - if (netcam->receiving->ptr != NULL) { - free(netcam->receiving->ptr); - } - free(netcam->receiving); - } - - if (netcam->jpegbuf != NULL) { - if (netcam->jpegbuf->ptr != NULL) { - free(netcam->jpegbuf->ptr); - } - free(netcam->jpegbuf); - } - - if (netcam->ftp != NULL) { - ftp_free_context(netcam->ftp); - } else { - netcam_disconnect(netcam); - } - - if (netcam->response != NULL) { - free(netcam->response); - } - - pthread_mutex_destroy(&netcam->mutex); - pthread_cond_destroy(&netcam->cap_cond); - pthread_cond_destroy(&netcam->pic_ready); - pthread_cond_destroy(&netcam->exiting); - free(netcam); + struct timespec waittime; + + if (!netcam) + return; + + /* + * This 'lock' is just a bit of "defensive" programming. It should + * only be necessary if the routine is being called from different + * threads, but in our Motion design, it should only be called from + * the motion main-loop. + */ + pthread_mutex_lock(&netcam->mutex); + + if (netcam->cnt->netcam == NULL) + return; + + /* + * We set the netcam_context pointer in the motion main-loop context + * to be NULL, so that this routine won't be called a second time + */ + netcam->cnt->netcam = NULL; + + /* + * Next we set 'finish' in order to get the camera-handler thread + * to stop. + */ + netcam->finish = 1; + + /* + * If the camera is non-streaming, the handler thread could be waiting + * for a signal, so we send it one. If it's actually waiting on the + * condition, it won't actually start yet because we still have + * netcam->mutex locked. + */ + + if (netcam->caps.streaming == NCS_UNSUPPORTED) + pthread_cond_signal(&netcam->cap_cond); + + + /* + * Once the camera-handler gets to the end of it's loop (probably as + * soon as we release netcam->mutex), because netcam->finish has been + * set it will exit it's loop, do anything it needs to do with the + * netcam context, and then send *us* as signal (netcam->exiting). + * Note that when we start our wait on netcam->exiting, our lock on + * netcam->mutex is automatically released, which will allow the + * handler to complete it's loop, notice that 'finish' is set and exit. + * This should always work, but again (defensive programming) we + * use pthread_cond_timedwait and, if our timeout (8 seconds) expires + * we just do the cleanup the handler would normally have done. This + * assures that (even if there is a bug in our code) motion will still + * be able to exit. + * If the init_retry_flag is not set the netcam_cleanup code was + * called while retrying the initial connection to a netcam and then + * there is no camera-handler started yet and thread_running must + * not be decremented. + */ + waittime.tv_sec = time(NULL) + 8; /* Seems that 3 is too small */ + waittime.tv_nsec = 0; + + if (!init_retry_flag && + pthread_cond_timedwait(&netcam->exiting, &netcam->mutex, &waittime) != 0) { + /* + * Although this shouldn't happen, if it *does* happen we will + * log it (just for the programmer's information) + */ + motion_log(-1, 0, "%s: No response from camera " + "handler - it must have already died", + __FUNCTION__); + pthread_mutex_lock(&global_lock); + threads_running--; + pthread_mutex_unlock(&global_lock); + } + + /* we don't need any lock anymore, so release it */ + pthread_mutex_unlock(&netcam->mutex); + + /* and cleanup the rest of the netcam_context structure */ + if (netcam->connect_host != NULL) + free(netcam->connect_host); + + if (netcam->connect_request != NULL) + free(netcam->connect_request); + + + if (netcam->boundary != NULL) + free(netcam->boundary); + + + if (netcam->latest != NULL) { + if (netcam->latest->ptr != NULL) + free(netcam->latest->ptr); + + free(netcam->latest); + } + + if (netcam->receiving != NULL) { + if (netcam->receiving->ptr != NULL) + free(netcam->receiving->ptr); + + free(netcam->receiving); + } + + if (netcam->jpegbuf != NULL) { + if (netcam->jpegbuf->ptr != NULL) + free(netcam->jpegbuf->ptr); + + free(netcam->jpegbuf); + } + + if (netcam->ftp != NULL) + ftp_free_context(netcam->ftp); + else + netcam_disconnect(netcam); + + + if (netcam->response != NULL) + free(netcam->response); + + pthread_mutex_destroy(&netcam->mutex); + pthread_cond_destroy(&netcam->cap_cond); + pthread_cond_destroy(&netcam->pic_ready); + pthread_cond_destroy(&netcam->exiting); + free(netcam); } /** @@ -2602,48 +2617,48 @@ void netcam_cleanup(netcam_context_ptr netcam, int init_retry_flag) */ int netcam_next(struct context *cnt, unsigned char *image) { - netcam_context_ptr netcam; - - /* - * Here we have some more "defensive programming". This check should - * never be true, but if it is just return with a "fatal error". - */ - if ((!cnt) || (!cnt->netcam)) - return NETCAM_FATAL_ERROR; - - netcam = cnt->netcam; - - if (!netcam->latest->used) { - if (debug_level) - motion_log(LOG_INFO, 0, "%s: netcam_next called with no data in buffer", - __FUNCTION__); - - return NETCAM_NOTHING_NEW_ERROR; - } - - /* - * If we are controlling a non-streaming camera, we synchronize the - * motion main-loop with the camera-handling thread through a signal, - * together with a flag to say "start your next capture". - */ - if (netcam->caps.streaming == NCS_UNSUPPORTED) { - pthread_mutex_lock(&netcam->mutex); - netcam->start_capture = 1; - pthread_cond_signal(&netcam->cap_cond); - pthread_mutex_unlock(&netcam->mutex); - } - - /* - * If an error occurs in the JPEG decompression which follows this, - * jpeglib will return to the code within this 'if'. Basically, our - * approach is to just return a NULL (failed) to the caller (an - * error message has already been produced by the libjpeg routines) - */ - if (setjmp(netcam->setjmp_buffer)) - return NETCAM_GENERAL_ERROR | NETCAM_JPEG_CONV_ERROR; - - /* If there was no error, process the latest image buffer */ - return netcam_proc_jpeg(netcam, image); + netcam_context_ptr netcam; + + /* + * Here we have some more "defensive programming". This check should + * never be true, but if it is just return with a "fatal error". + */ + if ((!cnt) || (!cnt->netcam)) + return NETCAM_FATAL_ERROR; + + netcam = cnt->netcam; + + if (!netcam->latest->used) { + if (debug_level) + motion_log(LOG_INFO, 0, "%s: netcam_next called with no data in buffer", + __FUNCTION__); + + return NETCAM_NOTHING_NEW_ERROR; + } + + /* + * If we are controlling a non-streaming camera, we synchronize the + * motion main-loop with the camera-handling thread through a signal, + * together with a flag to say "start your next capture". + */ + if (netcam->caps.streaming == NCS_UNSUPPORTED) { + pthread_mutex_lock(&netcam->mutex); + netcam->start_capture = 1; + pthread_cond_signal(&netcam->cap_cond); + pthread_mutex_unlock(&netcam->mutex); + } + + /* + * If an error occurs in the JPEG decompression which follows this, + * jpeglib will return to the code within this 'if'. Basically, our + * approach is to just return a NULL (failed) to the caller (an + * error message has already been produced by the libjpeg routines) + */ + if (setjmp(netcam->setjmp_buffer)) + return NETCAM_GENERAL_ERROR | NETCAM_JPEG_CONV_ERROR; + + /* If there was no error, process the latest image buffer */ + return netcam_proc_jpeg(netcam, image); } /** @@ -2664,232 +2679,233 @@ int netcam_next(struct context *cnt, unsigned char *image) int netcam_start(struct context *cnt) { - netcam_context_ptr netcam; /* local pointer to our context */ - pthread_attr_t handler_attribute; /* attributes of our handler thread */ - int retval; /* working var */ - struct url_t url; /* for parsing netcam URL */ - - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: entered netcam_start()", __FUNCTION__); - - memset(&url, 0, sizeof(url)); - if (SETUP) - motion_log(LOG_INFO, 0, "%s: Camera thread starting...", __FUNCTION__); - - /* - * Create a new netcam_context for this camera - * and clear all the entries. - */ - cnt->netcam = (struct netcam_context *) - mymalloc(sizeof(struct netcam_context)); - memset(cnt->netcam, 0, sizeof(struct netcam_context)); - netcam = cnt->netcam; /* Just for clarity in remaining code */ - netcam->cnt = cnt; /* Fill in the "parent" info */ - - /* - * Fill in our new netcam context with all known initial - * values. - */ - - /* Our image buffers */ - netcam->receiving = mymalloc(sizeof(netcam_buff)); - memset(netcam->receiving, 0, sizeof(netcam_buff)); - netcam->receiving->ptr = mymalloc(NETCAM_BUFFSIZE); - - netcam->jpegbuf = mymalloc(sizeof(netcam_buff)); - memset(netcam->jpegbuf, 0, sizeof(netcam_buff)); - netcam->jpegbuf->ptr = mymalloc(NETCAM_BUFFSIZE); - - netcam->latest = mymalloc(sizeof(netcam_buff)); - memset(netcam->latest, 0, sizeof(netcam_buff)); - netcam->latest->ptr = mymalloc(NETCAM_BUFFSIZE); - netcam->timeout.tv_sec = READ_TIMEOUT; - - /* Thread control structures */ - pthread_mutex_init(&netcam->mutex, NULL); - pthread_cond_init(&netcam->cap_cond, NULL); - pthread_cond_init(&netcam->pic_ready, NULL); - pthread_cond_init(&netcam->exiting, NULL); - - /* Initialise the average frame time to the user's value */ - netcam->av_frame_time = 1000000.0 / cnt->conf.frame_limit; - - /* - * If a proxy has been specified, parse that URL. - */ - if (cnt->conf.netcam_proxy) { - netcam_url_parse(&url, cnt->conf.netcam_proxy); - - if (!url.host) { - motion_log(LOG_ERR, 0, "%s: Invalid netcam_proxy (%s)", - __FUNCTION__, cnt->conf.netcam_proxy); - netcam_url_free(&url); - return -1; - } - - if (url.userpass) { - motion_log(LOG_ERR, 0, "%s: Username/password not allowed on a proxy URL", - __FUNCTION__); - netcam_url_free(&url); - return -1; - } - - /* - * A 'proxy' means that our eventual 'connect' to our - * camera must be sent to the proxy, and that our 'GET' must - * include the full path to the camera host. - */ - netcam->connect_host = url.host; - url.host = NULL; - netcam->connect_port = url.port; - netcam_url_free(&url); /* Finished with proxy */ - } - - /* - * Parse the URL from the configuration data - */ - netcam_url_parse(&url, cnt->conf.netcam_url); - - if (!url.host) { - motion_log(LOG_ERR, 0, "%s: Invalid netcam_url (%s)", - __FUNCTION__, cnt->conf.netcam_url); - netcam_url_free(&url); - return -1; - } - - if (cnt->conf.netcam_proxy == NULL) { - netcam->connect_host = url.host; - url.host = NULL; - netcam->connect_port = url.port; - } + netcam_context_ptr netcam; /* local pointer to our context */ + pthread_attr_t handler_attribute; /* attributes of our handler thread */ + int retval; /* working var */ + struct url_t url; /* for parsing netcam URL */ + + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: entered netcam_start()", __FUNCTION__); + + memset(&url, 0, sizeof(url)); + if (SETUP) + motion_log(LOG_INFO, 0, "%s: Camera thread starting...", __FUNCTION__); + + /* + * Create a new netcam_context for this camera + * and clear all the entries. + */ + cnt->netcam = (struct netcam_context *) + mymalloc(sizeof(struct netcam_context)); + memset(cnt->netcam, 0, sizeof(struct netcam_context)); + netcam = cnt->netcam; /* Just for clarity in remaining code */ + netcam->cnt = cnt; /* Fill in the "parent" info */ + + /* + * Fill in our new netcam context with all known initial + * values. + */ + + /* Our image buffers */ + netcam->receiving = mymalloc(sizeof(netcam_buff)); + memset(netcam->receiving, 0, sizeof(netcam_buff)); + netcam->receiving->ptr = mymalloc(NETCAM_BUFFSIZE); + + netcam->jpegbuf = mymalloc(sizeof(netcam_buff)); + memset(netcam->jpegbuf, 0, sizeof(netcam_buff)); + netcam->jpegbuf->ptr = mymalloc(NETCAM_BUFFSIZE); + + netcam->latest = mymalloc(sizeof(netcam_buff)); + memset(netcam->latest, 0, sizeof(netcam_buff)); + netcam->latest->ptr = mymalloc(NETCAM_BUFFSIZE); + netcam->timeout.tv_sec = READ_TIMEOUT; + + /* Thread control structures */ + pthread_mutex_init(&netcam->mutex, NULL); + pthread_cond_init(&netcam->cap_cond, NULL); + pthread_cond_init(&netcam->pic_ready, NULL); + pthread_cond_init(&netcam->exiting, NULL); + + /* Initialise the average frame time to the user's value */ + netcam->av_frame_time = 1000000.0 / cnt->conf.frame_limit; + + /* + * If a proxy has been specified, parse that URL. + */ + if (cnt->conf.netcam_proxy) { + netcam_url_parse(&url, cnt->conf.netcam_proxy); + + if (!url.host) { + motion_log(LOG_ERR, 0, "%s: Invalid netcam_proxy (%s)", + __FUNCTION__, cnt->conf.netcam_proxy); + netcam_url_free(&url); + return -1; + } + + if (url.userpass) { + motion_log(LOG_ERR, 0, "%s: Username/password not allowed on a proxy URL", + __FUNCTION__); + netcam_url_free(&url); + return -1; + } + + /* + * A 'proxy' means that our eventual 'connect' to our + * camera must be sent to the proxy, and that our 'GET' must + * include the full path to the camera host. + */ + netcam->connect_host = url.host; + url.host = NULL; + netcam->connect_port = url.port; + netcam_url_free(&url); /* Finished with proxy */ + } + + /* + * Parse the URL from the configuration data + */ + netcam_url_parse(&url, cnt->conf.netcam_url); + + if (!url.host) { + motion_log(LOG_ERR, 0, "%s: Invalid netcam_url (%s)", + __FUNCTION__, cnt->conf.netcam_url); + netcam_url_free(&url); + return -1; + } + + if (cnt->conf.netcam_proxy == NULL) { + netcam->connect_host = url.host; + url.host = NULL; + netcam->connect_port = url.port; + } /* Get HTTP Mode (1.0 default, 1.0 Keep-Alive, 1.1) flag from config - * and report its stata for debug reasons. - * The flags in the conf structure is read only and cannot be - * unset if the Keep-Alive needs to be switched off (ie. netcam does - * not turn out to support it. That is handled by unsetting the flags - * in the context structures (cnt->...) only. - */ - - if (!strcmp(cnt->conf.netcam_keepalive, "force")) { - netcam->connect_http_10 = TRUE; - netcam->connect_http_11 = FALSE; - netcam->connect_keepalive = TRUE; - } else if (!strcmp(cnt->conf.netcam_keepalive, "off")) { - netcam->connect_http_10 = TRUE; - netcam->connect_http_11 = FALSE; - netcam->connect_keepalive = FALSE; - } else if (!strcmp(cnt->conf.netcam_keepalive, "on")) { - netcam->connect_http_10 = FALSE; - netcam->connect_http_11 = TRUE; - netcam->connect_keepalive = TRUE; /* HTTP 1.1 has keepalive by default */ - } - if (debug_level > CAMERA_INFO) - motion_log(LOG_INFO, 0, "%s: Netcam_http parameter '%s' converts to flags: HTTP/1.0:" - "%s HTTP/1.1: %s Keep-Alive %s.", __FUNCTION__, cnt->conf.netcam_keepalive, - netcam->connect_http_10 ? "1":"0", netcam->connect_http_11 ? "1":"0", - netcam->connect_keepalive ? "ON":"OFF"); - - /* Initialise the netcam socket to -1 to trigger a connection by the keep-alive logic */ - netcam->sock = -1; - - if ((url.service) && (!strcmp(url.service, "http")) ) { - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: now calling netcam_setup_html()", __FUNCTION__); - - retval = netcam_setup_html(netcam, &url); - } else if ((url.service) && (!strcmp(url.service, "ftp")) ) { - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s:: now calling netcam_setup_ftp", __FUNCTION__); - - retval = netcam_setup_ftp(netcam, &url); - } else if ((url.service) && (!strcmp(url.service, "file")) ) { - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: now calling netcam_setup_file()", __FUNCTION__); - - retval = netcam_setup_file(netcam, &url); - } else if ((url.service) && (!strcmp(url.service, "mjpg")) ) { - if (debug_level > CAMERA_INFO) - motion_log(-1, 0, "%s: now calling netcam_setup_mjpg()", __FUNCTION__); - - strcpy(url.service, "http"); /* Put back a real URL service. */ - retval = netcam_setup_mjpg(netcam, &url); - } else { - motion_log(LOG_ERR, 0, "%s: Invalid netcam service '%s' - " - "must be http, ftp, mjpg or file.", __FUNCTION__, url.service); - netcam_url_free(&url); - return -1; - } - - if (retval < 0) - return -1; - - /* - * We expect that, at this point, we should be positioned to read - * the first image available from the camera (directly after the - * applicable header). We want to decode the image in order to get - * the dimensions (width and height). If successful, we will use - * these to set the required image buffer(s) in our netcam_struct. - */ - if ((retval = netcam->get_image(netcam)) != 0) { - motion_log(LOG_ERR, 0, "%s: Failed trying to read first image - retval:%d", - __FUNCTION__, retval); - return -1; - } - - /* - * If an error occurs in the JPEG decompression which follows this, - * jpeglib will return to the code within this 'if'. If such an error - * occurs during startup, we will just abandon this attempt. - */ - if (setjmp(netcam->setjmp_buffer)) { - motion_log(LOG_ERR, 0, "%s: libjpeg decompression failure " - "on first frame - giving up!", __FUNCTION__); - return -1; - } - - netcam->netcam_broken = cnt->conf.netcam_broken; - netcam->JFIF_marker = 0; - netcam_get_dimensions(netcam); - - /* Motion currently requires that image height and width is a - * multiple of 16. So we check for this. - */ - if (netcam->width % 16) { - motion_log(LOG_ERR, 0, "%s: netcam image width (%d) is not modulo 16", - __FUNCTION__, netcam->width); - return -1; - } - - if (netcam->height % 16) { - motion_log(LOG_ERR, 0, "%s: netcam image height (%d) is not modulo 16", - __FUNCTION__, netcam->height); - return -1; - } - - /* Fill in camera details into context structure */ - cnt->imgs.width = netcam->width; - cnt->imgs.height = netcam->height; - cnt->imgs.size = (netcam->width * netcam->height * 3) / 2; - cnt->imgs.motionsize = netcam->width * netcam->height; - cnt->imgs.type = VIDEO_PALETTE_YUV420P; - - /* - * Everything is now ready - start up the - * "handler thread". - */ - pthread_attr_init(&handler_attribute); - pthread_attr_setdetachstate(&handler_attribute, PTHREAD_CREATE_DETACHED); - pthread_mutex_lock(&global_lock); - netcam->threadnr = ++threads_running; - pthread_mutex_unlock(&global_lock); - - if ((retval = pthread_create(&netcam->thread_id, &handler_attribute, - &netcam_handler_loop, netcam)) < 0) { - motion_log(LOG_ERR, 1, "%s: Starting camera handler thread [%d]", - __FUNCTION__, netcam->threadnr); - return -1; - } - - return 0; + * and report its stata for debug reasons. + * The flags in the conf structure is read only and cannot be + * unset if the Keep-Alive needs to be switched off (ie. netcam does + * not turn out to support it. That is handled by unsetting the flags + * in the context structures (cnt->...) only. + */ + + if (!strcmp(cnt->conf.netcam_keepalive, "force")) { + netcam->connect_http_10 = TRUE; + netcam->connect_http_11 = FALSE; + netcam->connect_keepalive = TRUE; + } else if (!strcmp(cnt->conf.netcam_keepalive, "off")) { + netcam->connect_http_10 = TRUE; + netcam->connect_http_11 = FALSE; + netcam->connect_keepalive = FALSE; + } else if (!strcmp(cnt->conf.netcam_keepalive, "on")) { + netcam->connect_http_10 = FALSE; + netcam->connect_http_11 = TRUE; + netcam->connect_keepalive = TRUE; /* HTTP 1.1 has keepalive by default */ + } + + if (debug_level > CAMERA_INFO) + motion_log(LOG_INFO, 0, "%s: Netcam_http parameter '%s' converts to flags: HTTP/1.0:" + "%s HTTP/1.1: %s Keep-Alive %s.", __FUNCTION__, cnt->conf.netcam_keepalive, + netcam->connect_http_10 ? "1":"0", netcam->connect_http_11 ? "1":"0", + netcam->connect_keepalive ? "ON":"OFF"); + + /* Initialise the netcam socket to -1 to trigger a connection by the keep-alive logic */ + netcam->sock = -1; + + if ((url.service) && (!strcmp(url.service, "http"))) { + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: now calling netcam_setup_html()", __FUNCTION__); + + retval = netcam_setup_html(netcam, &url); + } else if ((url.service) && (!strcmp(url.service, "ftp"))) { + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s:: now calling netcam_setup_ftp", __FUNCTION__); + + retval = netcam_setup_ftp(netcam, &url); + } else if ((url.service) && (!strcmp(url.service, "file"))) { + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: now calling netcam_setup_file()", __FUNCTION__); + + retval = netcam_setup_file(netcam, &url); + } else if ((url.service) && (!strcmp(url.service, "mjpg"))) { + if (debug_level > CAMERA_INFO) + motion_log(-1, 0, "%s: now calling netcam_setup_mjpg()", __FUNCTION__); + + strcpy(url.service, "http"); /* Put back a real URL service. */ + retval = netcam_setup_mjpg(netcam, &url); + } else { + motion_log(LOG_ERR, 0, "%s: Invalid netcam service '%s' - " + "must be http, ftp, mjpg or file.", __FUNCTION__, url.service); + netcam_url_free(&url); + return -1; + } + + if (retval < 0) + return -1; + + /* + * We expect that, at this point, we should be positioned to read + * the first image available from the camera (directly after the + * applicable header). We want to decode the image in order to get + * the dimensions (width and height). If successful, we will use + * these to set the required image buffer(s) in our netcam_struct. + */ + if ((retval = netcam->get_image(netcam)) != 0) { + motion_log(LOG_ERR, 0, "%s: Failed trying to read first image - retval:%d", + __FUNCTION__, retval); + return -1; + } + + /* + * If an error occurs in the JPEG decompression which follows this, + * jpeglib will return to the code within this 'if'. If such an error + * occurs during startup, we will just abandon this attempt. + */ + if (setjmp(netcam->setjmp_buffer)) { + motion_log(LOG_ERR, 0, "%s: libjpeg decompression failure " + "on first frame - giving up!", __FUNCTION__); + return -1; + } + + netcam->netcam_broken = cnt->conf.netcam_broken; + netcam->JFIF_marker = 0; + netcam_get_dimensions(netcam); + + /* Motion currently requires that image height and width is a + * multiple of 16. So we check for this. + */ + if (netcam->width % 16) { + motion_log(LOG_ERR, 0, "%s: netcam image width (%d) is not modulo 16", + __FUNCTION__, netcam->width); + return -1; + } + + if (netcam->height % 16) { + motion_log(LOG_ERR, 0, "%s: netcam image height (%d) is not modulo 16", + __FUNCTION__, netcam->height); + return -1; + } + + /* Fill in camera details into context structure */ + cnt->imgs.width = netcam->width; + cnt->imgs.height = netcam->height; + cnt->imgs.size = (netcam->width * netcam->height * 3) / 2; + cnt->imgs.motionsize = netcam->width * netcam->height; + cnt->imgs.type = VIDEO_PALETTE_YUV420P; + + /* + * Everything is now ready - start up the + * "handler thread". + */ + pthread_attr_init(&handler_attribute); + pthread_attr_setdetachstate(&handler_attribute, PTHREAD_CREATE_DETACHED); + pthread_mutex_lock(&global_lock); + netcam->threadnr = ++threads_running; + pthread_mutex_unlock(&global_lock); + + if ((retval = pthread_create(&netcam->thread_id, &handler_attribute, + &netcam_handler_loop, netcam)) < 0) { + motion_log(LOG_ERR, 1, "%s: Starting camera handler thread [%d]", + __FUNCTION__, netcam->threadnr); + return -1; + } + + return 0; } diff --git a/netcam_ftp.c b/netcam_ftp.c index 0c70183..acd7435 100644 --- a/netcam_ftp.c +++ b/netcam_ftp.c @@ -12,10 +12,7 @@ #include #include -//#include -//#include #include - #include "netcam_ftp.h" /** @@ -32,15 +29,15 @@ */ ftp_context_pointer ftp_new_context(void) { - ftp_context_pointer ret; + ftp_context_pointer ret; - /* note that mymalloc will exit on any problem */ - ret = mymalloc(sizeof(ftp_context)); + /* note that mymalloc will exit on any problem */ + ret = mymalloc(sizeof(ftp_context)); - memset(ret, 0, sizeof(ftp_context)); - ret->control_file_desc = -1; /* no control connection yet */ - ret->data_file_desc = -1; /* no data connection yet */ - return ret; + memset(ret, 0, sizeof(ftp_context)); + ret->control_file_desc = -1; /* no control connection yet */ + ret->data_file_desc = -1; /* no data connection yet */ + return ret; } /** @@ -57,22 +54,22 @@ ftp_context_pointer ftp_new_context(void) */ void ftp_free_context(ftp_context_pointer ctxt) { - if (ctxt == NULL) - return; + if (ctxt == NULL) + return; - if (ctxt->path != NULL) - free(ctxt->path); + if (ctxt->path != NULL) + free(ctxt->path); - if (ctxt->user) - free(ctxt->user); + if (ctxt->user) + free(ctxt->user); - if (ctxt->passwd) - free(ctxt->passwd); + if (ctxt->passwd) + free(ctxt->passwd); - if (ctxt->control_file_desc >= 0) - close(ctxt->control_file_desc); + if (ctxt->control_file_desc >= 0) + close(ctxt->control_file_desc); - free(ctxt); + free(ctxt); } /** @@ -91,35 +88,36 @@ void ftp_free_context(ftp_context_pointer ctxt) * -XXX for response to be continued */ static int ftp_parse_response(char *buf, int len) { - int val = 0; + int val = 0; + + if (len < 3) + return(-1); - if (len < 3) - return(-1); - if ((*buf >= '0') && (*buf <= '9')) - val = val * 10 + (*buf - '0'); - else - return(0); + if ((*buf >= '0') && (*buf <= '9')) + val = val * 10 + (*buf - '0'); + else + return(0); - buf++; + buf++; - if ((*buf >= '0') && (*buf <= '9')) - val = val * 10 + (*buf - '0'); - else - return(0); + if ((*buf >= '0') && (*buf <= '9')) + val = val * 10 + (*buf - '0'); + else + return(0); - buf++; + buf++; - if ((*buf >= '0') && (*buf <= '9')) - val = val * 10 + (*buf - '0'); - else - return(0); + if ((*buf >= '0') && (*buf <= '9')) + val = val * 10 + (*buf - '0'); + else + return(0); - buf++; + buf++; - if (*buf == '-') - return(-val); + if (*buf == '-') + return(-val); - return(val); + return(val); } /** @@ -134,53 +132,53 @@ static int ftp_parse_response(char *buf, int len) { * Returns the number of bytes read, < 0 indicates an error */ static int ftp_get_more(ftp_context_pointer ctxt) { - int len; - int size; - - /* Validate that our context structure is valid */ - if ((ctxt == NULL) || (ctxt->control_file_desc < 0)) - return(-1); - - if ((ctxt->control_buffer_index < 0) || (ctxt->control_buffer_index > FTP_BUF_SIZE)) - return(-1); - - if ((ctxt->control_buffer_used < 0) || (ctxt->control_buffer_used > FTP_BUF_SIZE)) - return(-1); - - if (ctxt->control_buffer_index > ctxt->control_buffer_used) - return(-1); - - /* - * First pack the control buffer - */ - if (ctxt->control_buffer_index > 0) { - memmove(&ctxt->control_buffer[0], - &ctxt->control_buffer[ctxt->control_buffer_index], - ctxt->control_buffer_used - ctxt->control_buffer_index); - ctxt->control_buffer_used -= ctxt->control_buffer_index; - ctxt->control_buffer_index = 0; - } - size = FTP_BUF_SIZE - ctxt->control_buffer_used; - - if (size == 0) { - return(0); - } - - /* - * Read the amount left on the control connection - */ - if ((len = recv(ctxt->control_file_desc, - &ctxt->control_buffer[ctxt->control_buffer_index], - size, 0)) < 0) { - motion_log(LOG_ERR, 1, "%s: recv failed in ftp_get_more", __FUNCTION__); - close(ctxt->control_file_desc); - ctxt->control_file_desc = -1; - return(-1); - } - ctxt->control_buffer_used += len; - ctxt->control_buffer[ctxt->control_buffer_used] = 0; - - return(len); + int len; + int size; + + /* Validate that our context structure is valid */ + if ((ctxt == NULL) || (ctxt->control_file_desc < 0)) + return(-1); + + if ((ctxt->control_buffer_index < 0) || (ctxt->control_buffer_index > FTP_BUF_SIZE)) + return(-1); + + if ((ctxt->control_buffer_used < 0) || (ctxt->control_buffer_used > FTP_BUF_SIZE)) + return(-1); + + if (ctxt->control_buffer_index > ctxt->control_buffer_used) + return(-1); + + /* + * First pack the control buffer + */ + if (ctxt->control_buffer_index > 0) { + memmove(&ctxt->control_buffer[0], + &ctxt->control_buffer[ctxt->control_buffer_index], + ctxt->control_buffer_used - ctxt->control_buffer_index); + ctxt->control_buffer_used -= ctxt->control_buffer_index; + ctxt->control_buffer_index = 0; + } + + size = FTP_BUF_SIZE - ctxt->control_buffer_used; + + if (size == 0) + return(0); + + /* + * Read the amount left on the control connection + */ + if ((len = recv(ctxt->control_file_desc, + &ctxt->control_buffer[ctxt->control_buffer_index], size, 0)) < 0) { + motion_log(LOG_ERR, 1, "%s: recv failed in ftp_get_more", __FUNCTION__); + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + return(-1); + } + + ctxt->control_buffer_used += len; + ctxt->control_buffer[ctxt->control_buffer_used] = 0; + + return(len); } /** @@ -195,57 +193,64 @@ static int ftp_get_more(ftp_context_pointer ctxt) { * Returns the code number */ static int ftp_get_response(ftp_context_pointer ctxt) { - char *ptr, *end; - int len; - int res = -1, cur = -1; - - if ((ctxt == NULL) || (ctxt->control_file_desc < 0)) - return(-1); - - get_more: - /* - * Assumes everything up to control_buffer[control_buffer_index] has been read - * and analyzed. - */ - len = ftp_get_more(ctxt); - if (len < 0) { - return(-1); - } - if ((ctxt->control_buffer_used == 0) && (len == 0)) { - return(-1); - } - ptr = &ctxt->control_buffer[ctxt->control_buffer_index]; - end = &ctxt->control_buffer[ctxt->control_buffer_used]; - - while (ptr < end) { - cur = ftp_parse_response(ptr, end - ptr); - if (cur > 0) { - /* - * Successfully scanned the control code, skip - * till the end of the line, but keep the index to be - * able to analyze the result if needed. - */ - res = cur; - ptr += 3; - ctxt->control_buffer_answer = ptr - ctxt->control_buffer; - while ((ptr < end) && (*ptr != '\n')) ptr++; - if (*ptr == '\n') ptr++; - if (*ptr == '\r') ptr++; - break; - } - while ((ptr < end) && (*ptr != '\n')) - ptr++; - if (ptr >= end) { - ctxt->control_buffer_index = ctxt->control_buffer_used; - goto get_more; - } - if (*ptr != '\r') ptr++; - } - - if (res < 0) - goto get_more; - ctxt->control_buffer_index = ptr - ctxt->control_buffer; - return(res / 100); + char *ptr, *end; + int len; + int res = -1, cur = -1; + + if ((ctxt == NULL) || (ctxt->control_file_desc < 0)) + return(-1); + + get_more: + /* + * Assumes everything up to control_buffer[control_buffer_index] has been read + * and analyzed. + */ + len = ftp_get_more(ctxt); + if (len < 0) { + return(-1); + } + + if ((ctxt->control_buffer_used == 0) && (len == 0)) { + return(-1); + } + + ptr = &ctxt->control_buffer[ctxt->control_buffer_index]; + end = &ctxt->control_buffer[ctxt->control_buffer_used]; + + while (ptr < end) { + cur = ftp_parse_response(ptr, end - ptr); + if (cur > 0) { + /* + * Successfully scanned the control code, skip + * till the end of the line, but keep the index to be + * able to analyze the result if needed. + */ + res = cur; + ptr += 3; + ctxt->control_buffer_answer = ptr - ctxt->control_buffer; + while ((ptr < end) && (*ptr != '\n')) ptr++; + if (*ptr == '\n') ptr++; + if (*ptr == '\r') ptr++; + break; + } + + while ((ptr < end) && (*ptr != '\n')) + ptr++; + + if (ptr >= end) { + ctxt->control_buffer_index = ctxt->control_buffer_used; + goto get_more; + } + if (*ptr != '\r') + ptr++; + } + + if (res < 0) + goto get_more; + + ctxt->control_buffer_index = ptr - ctxt->control_buffer; + + return(res / 100); } /** @@ -253,22 +258,24 @@ static int ftp_get_response(ftp_context_pointer ctxt) { */ static int ftp_send_user(ftp_context_pointer ctxt) { - char buf[200]; - int len; - int res; - - if (ctxt->user == NULL) - snprintf(buf, sizeof(buf), "USER anonymous\r\n"); - else - snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user); - buf[sizeof(buf) - 1] = 0; - len = strlen(buf); - res = send(ctxt->control_file_desc, buf, len, 0); - if (res < 0) { - motion_log(LOG_ERR, 1, "%s: send failed in ftp_send_user", __FUNCTION__); - return(res); - } - return(0); + char buf[200]; + int len; + int res; + + if (ctxt->user == NULL) + snprintf(buf, sizeof(buf), "USER anonymous\r\n"); + else + snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user); + + buf[sizeof(buf) - 1] = 0; + len = strlen(buf); + res = send(ctxt->control_file_desc, buf, len, 0); + + if (res < 0) { + motion_log(LOG_ERR, 1, "%s: send failed in ftp_send_user", __FUNCTION__); + return(res); + } + return(0); } /** @@ -276,22 +283,25 @@ static int ftp_send_user(ftp_context_pointer ctxt) { */ static int ftp_send_passwd(ftp_context_pointer ctxt) { - char buf[200]; - int len; - int res; - - if (ctxt->passwd == NULL) - snprintf(buf, sizeof(buf), "PASS anonymous@\r\n"); - else - snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd); - buf[sizeof(buf) - 1] = 0; - len = strlen(buf); - res = send(ctxt->control_file_desc, buf, len, 0); - if (res < 0) { - motion_log(LOG_ERR, 1, "%s: send failed in ftp_send_passwd", __FUNCTION__); - return(res); - } - return(0); + char buf[200]; + int len; + int res; + + if (ctxt->passwd == NULL) + snprintf(buf, sizeof(buf), "PASS anonymous@\r\n"); + else + snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd); + + buf[sizeof(buf) - 1] = 0; + len = strlen(buf); + res = send(ctxt->control_file_desc, buf, len, 0); + + if (res < 0) { + motion_log(LOG_ERR, 1, "%s: send failed in ftp_send_passwd", __FUNCTION__); + return(res); + } + + return(0); } /** @@ -308,20 +318,22 @@ static int ftp_send_passwd(ftp_context_pointer ctxt) { static int ftp_quit(ftp_context_pointer ctxt) { - char buf[200]; - int len, res; - - if ((ctxt == NULL) || (ctxt->control_file_desc < 0)) - return(-1); - - snprintf(buf, sizeof(buf), "QUIT\r\n"); - len = strlen(buf); - res = send(ctxt->control_file_desc, buf, len, 0); - if (res < 0) { - motion_log(LOG_ERR, 1, "%s: send failed in ftp_quit", __FUNCTION__); - return(res); - } - return(0); + char buf[200]; + int len, res; + + if ((ctxt == NULL) || (ctxt->control_file_desc < 0)) + return(-1); + + snprintf(buf, sizeof(buf), "QUIT\r\n"); + len = strlen(buf); + res = send(ctxt->control_file_desc, buf, len, 0); + + if (res < 0) { + motion_log(LOG_ERR, 1, "%s: send failed in ftp_quit", __FUNCTION__); + return(res); + } + + return(0); } /** @@ -337,123 +349,137 @@ static int ftp_quit(ftp_context_pointer ctxt) { */ int ftp_connect(netcam_context_ptr netcam) { - ftp_context_pointer ctxt; - struct hostent *hp; - int port; - int res; - int addrlen = sizeof (struct sockaddr_in); - - if (netcam == NULL) - return -1; - ctxt = netcam->ftp; - if (ctxt == NULL) - return(-1); - if (netcam->connect_host == NULL) - return(-1); - - /* - * do the blocking DNS query. - */ - port = netcam->connect_port; - if (port == 0) - port = 21; - - memset (&ctxt->ftp_address, 0, sizeof(ctxt->ftp_address)); - - hp = gethostbyname (netcam->connect_host); - if (hp == NULL) { - motion_log(LOG_ERR, 1, "%s: gethostbyname failed in ftp_connect", __FUNCTION__); - return (-1); - } - if ((unsigned int) hp->h_length > - sizeof(((struct sockaddr_in *)&ctxt->ftp_address)->sin_addr)) { - motion_log(LOG_ERR, 1, "%s: gethostbyname address mismatch " - "in ftp_connect", __FUNCTION__); - return (-1); - } - - /* - * Prepare the socket - */ - ((struct sockaddr_in *)&ctxt->ftp_address)->sin_family = AF_INET; - memcpy (&((struct sockaddr_in *)&ctxt->ftp_address)->sin_addr, hp->h_addr_list[0], hp->h_length); - ((struct sockaddr_in *)&ctxt->ftp_address)->sin_port = (u_short)htons ((unsigned short)port); - ctxt->control_file_desc = socket (AF_INET, SOCK_STREAM, 0); - addrlen = sizeof (struct sockaddr_in); - - if (ctxt->control_file_desc < 0) { - motion_log(LOG_ERR, 1, "%s: socket failed", __FUNCTION__); - return(-1); - } - - /* - * Do the connect. - */ - if (connect(ctxt->control_file_desc, (struct sockaddr *) &ctxt->ftp_address, - addrlen) < 0) { - motion_log(LOG_ERR, 1, "%s: Failed to create a connection", __FUNCTION__); - close(ctxt->control_file_desc); - ctxt->control_file_desc = -1; - return(-1); - } - - /* - * Wait for the HELLO from the server. - */ - res = ftp_get_response(ctxt); - if (res != 2) { - close(ctxt->control_file_desc); - ctxt->control_file_desc = -1; - return(-1); - } - - /* - * Do the authentication - */ - res = ftp_send_user(ctxt); - if (res < 0) { - close(ctxt->control_file_desc); - ctxt->control_file_desc = -1; - return(-1); - } - res = ftp_get_response(ctxt); - switch (res) { - case 2: - return(0); - case 3: - break; - case 1: - case 4: - case 5: - case -1: - default: - close(ctxt->control_file_desc); - ctxt->control_file_desc = -1; - return(-1); - } - res = ftp_send_passwd(ctxt); - if (res < 0) { - close(ctxt->control_file_desc); - ctxt->control_file_desc = -1; - return(-1); - } - res = ftp_get_response(ctxt); - switch (res) { - case 2: - break; - case 3: - motion_log(LOG_ERR, 0, "%s: FTP server asking for ACCT on anonymous", __FUNCTION__); - case 1: - case 4: - case 5: - case -1: - default: - close(ctxt->control_file_desc); ctxt->control_file_desc = -1; - ctxt->control_file_desc = -1; - return(-1); - } - - return(0); + ftp_context_pointer ctxt; + struct hostent *hp; + int port; + int res; + int addrlen = sizeof (struct sockaddr_in); + + if (netcam == NULL) + return -1; + + ctxt = netcam->ftp; + + if (ctxt == NULL) + return(-1); + + if (netcam->connect_host == NULL) + return(-1); + + /* + * do the blocking DNS query. + */ + port = netcam->connect_port; + + if (port == 0) + port = 21; + + memset (&ctxt->ftp_address, 0, sizeof(ctxt->ftp_address)); + + hp = gethostbyname (netcam->connect_host); + + if (hp == NULL) { + motion_log(LOG_ERR, 1, "%s: gethostbyname failed in ftp_connect", __FUNCTION__); + return (-1); + } + + if ((unsigned int) hp->h_length > + sizeof(((struct sockaddr_in *)&ctxt->ftp_address)->sin_addr)) { + motion_log(LOG_ERR, 1, "%s: gethostbyname address mismatch " + "in ftp_connect", __FUNCTION__); + return (-1); + } + + /* + * Prepare the socket + */ + ((struct sockaddr_in *)&ctxt->ftp_address)->sin_family = AF_INET; + memcpy (&((struct sockaddr_in *)&ctxt->ftp_address)->sin_addr, hp->h_addr_list[0], hp->h_length); + ((struct sockaddr_in *)&ctxt->ftp_address)->sin_port = (u_short)htons ((unsigned short)port); + ctxt->control_file_desc = socket (AF_INET, SOCK_STREAM, 0); + addrlen = sizeof (struct sockaddr_in); + + if (ctxt->control_file_desc < 0) { + motion_log(LOG_ERR, 1, "%s: socket failed", __FUNCTION__); + return(-1); + } + + /* + * Do the connect. + */ + if (connect(ctxt->control_file_desc, (struct sockaddr *) &ctxt->ftp_address, + addrlen) < 0) { + motion_log(LOG_ERR, 1, "%s: Failed to create a connection", __FUNCTION__); + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + return(-1); + } + + /* + * Wait for the HELLO from the server. + */ + res = ftp_get_response(ctxt); + + if (res != 2) { + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + return(-1); + } + + /* + * Do the authentication + */ + res = ftp_send_user(ctxt); + + if (res < 0) { + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + return(-1); + } + + res = ftp_get_response(ctxt); + + switch (res) { + case 2: + return(0); + case 3: + break; + case 1: + case 4: + case 5: + case -1: + default: + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + return(-1); + } + + res = ftp_send_passwd(ctxt); + + if (res < 0) { + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + return(-1); + } + + res = ftp_get_response(ctxt); + switch (res) { + case 2: + break; + case 3: + motion_log(LOG_ERR, 0, "%s: FTP server asking for ACCT on anonymous", __FUNCTION__); + case 1: + case 4: + case 5: + case -1: + default: + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + ctxt->control_file_desc = -1; + return(-1); + } + + return(0); } /** @@ -469,152 +495,162 @@ int ftp_connect(netcam_context_ptr netcam) { */ static int ftp_get_connection(ftp_context_pointer ctxt) { - char buf[200], *cur; - int len, i; - int res; - int on; - unsigned char ad[6], *adp, *portp; - unsigned int temp[6]; - struct sockaddr_in data_address; - unsigned int data_address_length; - - if (ctxt == NULL) - return(-1); - - /* set up a socket for our data address */ - if (ctxt->data_file_desc != -1) - close(ctxt->data_file_desc); - memset (&data_address, 0, sizeof(data_address)); - ctxt->data_file_desc = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (ctxt->data_file_desc < 0) { - motion_log(LOG_ERR, 1, "%s: socket failed", __FUNCTION__); - return (-1); - } - on = 1; - if (setsockopt(ctxt->data_file_desc, SOL_SOCKET, SO_REUSEADDR, - (char *)&on, sizeof(on)) < 0) { - motion_log(LOG_ERR, 1, "%s: setting socket option SO_REUSEADDR", __FUNCTION__); - return -1; - } - - ((struct sockaddr_in *)&data_address)->sin_family = AF_INET; - data_address_length = sizeof (struct sockaddr_in); - - if (ctxt->passive) { - /* send PASV command over control channel */ - snprintf (buf, sizeof(buf), "PASV\r\n"); - len = strlen (buf); - res = send(ctxt->control_file_desc, buf, len, 0); - if (res < 0) { - motion_log(LOG_ERR, 1, "%s: send failed in ftp_get_connection", __FUNCTION__); - close(ctxt->data_file_desc); - ctxt->data_file_desc = -1; - return(res); - } - /* check server's answer */ - res = ftp_get_response(ctxt); - if (res != 2) { - if (res == 5) { - close(ctxt->data_file_desc); - ctxt->data_file_desc = -1; - return(-1); - } else { - /* - * retry with an active connection - */ - close(ctxt->data_file_desc); - ctxt->data_file_desc = -1; - ctxt->passive = 0; - } - } - /* parse the IP address and port supplied by the server */ - cur = &ctxt->control_buffer[ctxt->control_buffer_answer]; - while (((*cur < '0') || (*cur > '9')) && *cur != '\0') - cur++; - if (sscanf (cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2], - &temp[3], &temp[4], &temp[5]) != 6) { - motion_log(LOG_ERR, 0, "%s: Invalid answer to PASV", __FUNCTION__); - if (ctxt->data_file_desc != -1) { - close (ctxt->data_file_desc); - ctxt->data_file_desc = -1; - } - return (-1); - } - for (i = 0; i < 6; i++) - ad[i] = (unsigned char) (temp[i] & 0xff) ; - memcpy (&((struct sockaddr_in *)&data_address)->sin_addr, &ad[0], 4); - memcpy (&((struct sockaddr_in *)&data_address)->sin_port, &ad[4], 2); - - /* Now try to connect to the data port */ - if (connect(ctxt->data_file_desc, (struct sockaddr *) &data_address, - data_address_length) < 0) { - motion_log(LOG_ERR, 1, "%s: Failed to create a data connection", __FUNCTION__); - close(ctxt->data_file_desc); - ctxt->data_file_desc = -1; - return (-1); - } - } else { - /* - * We want to bind to a port to receive the data. To do this, - * we need the address of our host. One easy way to get it is - * to get the info from the control connection that we have - * with the remote server - */ - getsockname(ctxt->control_file_desc, (struct sockaddr *)&data_address, - &data_address_length); - ((struct sockaddr_in *)&data_address)->sin_port = 0; - - /* bind to the socket - should give us a unique port */ - if (bind(ctxt->data_file_desc, (struct sockaddr *) &data_address, - data_address_length) < 0) { - motion_log(LOG_ERR, 1, "%s: bind failed", __FUNCTION__); - close(ctxt->data_file_desc); - ctxt->data_file_desc = -1; - return (-1); - } - - /* we get the port number by reading back in the sockaddr */ - getsockname(ctxt->data_file_desc, (struct sockaddr *)&data_address, - &data_address_length); - - /* set up a 'listen' on the port to get the server's connection */ - if (listen(ctxt->data_file_desc, 1) < 0) { - motion_log(LOG_ERR, 1, "%s: listen failed", __FUNCTION__); - close(ctxt->data_file_desc); - ctxt->data_file_desc = -1; - return (-1); - } - - /* now generate the PORT command */ - adp = (unsigned char *) &((struct sockaddr_in *)&data_address)->sin_addr; - portp = (unsigned char *) &((struct sockaddr_in *)&data_address)->sin_port; - snprintf (buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n", - adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff, - portp[0] & 0xff, portp[1] & 0xff); - - buf[sizeof(buf) - 1] = 0; - len = strlen(buf); - - /* send the PORT command to the server */ - res = send(ctxt->control_file_desc, buf, len, 0); - - if (res < 0) { - motion_log(LOG_ERR, 1, "%s: send failed in ftp_get_connection", __FUNCTION__); - close(ctxt->data_file_desc); - ctxt->data_file_desc = -1; - return(res); - } - - res = ftp_get_response(ctxt); - - if (res != 2) { - close(ctxt->data_file_desc); - ctxt->data_file_desc = -1; - return(-1); - } - } - - return(ctxt->data_file_desc); + char buf[200], *cur; + int len, i; + int res; + int on; + unsigned char ad[6], *adp, *portp; + unsigned int temp[6]; + struct sockaddr_in data_address; + unsigned int data_address_length; + + if (ctxt == NULL) + return(-1); + + /* set up a socket for our data address */ + if (ctxt->data_file_desc != -1) + close(ctxt->data_file_desc); + + memset (&data_address, 0, sizeof(data_address)); + ctxt->data_file_desc = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (ctxt->data_file_desc < 0) { + motion_log(LOG_ERR, 1, "%s: socket failed", __FUNCTION__); + return (-1); + } + + on = 1; + + if (setsockopt(ctxt->data_file_desc, SOL_SOCKET, SO_REUSEADDR, + (char *)&on, sizeof(on)) < 0) { + motion_log(LOG_ERR, 1, "%s: setting socket option SO_REUSEADDR", __FUNCTION__); + return -1; + } + + ((struct sockaddr_in *)&data_address)->sin_family = AF_INET; + data_address_length = sizeof (struct sockaddr_in); + + if (ctxt->passive) { + /* send PASV command over control channel */ + snprintf (buf, sizeof(buf), "PASV\r\n"); + len = strlen (buf); + res = send(ctxt->control_file_desc, buf, len, 0); + + if (res < 0) { + motion_log(LOG_ERR, 1, "%s: send failed in ftp_get_connection", __FUNCTION__); + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return(res); + } + /* check server's answer */ + res = ftp_get_response(ctxt); + + if (res != 2) { + if (res == 5) { + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return(-1); + } else { + /* + * retry with an active connection + */ + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + ctxt->passive = 0; + } + } + /* parse the IP address and port supplied by the server */ + cur = &ctxt->control_buffer[ctxt->control_buffer_answer]; + + while (((*cur < '0') || (*cur > '9')) && *cur != '\0') + cur++; + + if (sscanf (cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2], + &temp[3], &temp[4], &temp[5]) != 6) { + motion_log(LOG_ERR, 0, "%s: Invalid answer to PASV", __FUNCTION__); + if (ctxt->data_file_desc != -1) { + close (ctxt->data_file_desc); + ctxt->data_file_desc = -1; + } + return (-1); + } + + for (i = 0; i < 6; i++) + ad[i] = (unsigned char) (temp[i] & 0xff) ; + + memcpy (&((struct sockaddr_in *)&data_address)->sin_addr, &ad[0], 4); + memcpy (&((struct sockaddr_in *)&data_address)->sin_port, &ad[4], 2); + + /* Now try to connect to the data port */ + if (connect(ctxt->data_file_desc, (struct sockaddr *) &data_address, + data_address_length) < 0) { + motion_log(LOG_ERR, 1, "%s: Failed to create a data connection", __FUNCTION__); + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return (-1); + } + } else { + /* + * We want to bind to a port to receive the data. To do this, + * we need the address of our host. One easy way to get it is + * to get the info from the control connection that we have + * with the remote server + */ + getsockname(ctxt->control_file_desc, (struct sockaddr *)&data_address, + &data_address_length); + ((struct sockaddr_in *)&data_address)->sin_port = 0; + + /* bind to the socket - should give us a unique port */ + if (bind(ctxt->data_file_desc, (struct sockaddr *) &data_address, + data_address_length) < 0) { + motion_log(LOG_ERR, 1, "%s: bind failed", __FUNCTION__); + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return (-1); + } + + /* we get the port number by reading back in the sockaddr */ + getsockname(ctxt->data_file_desc, (struct sockaddr *)&data_address, + &data_address_length); + + /* set up a 'listen' on the port to get the server's connection */ + if (listen(ctxt->data_file_desc, 1) < 0) { + motion_log(LOG_ERR, 1, "%s: listen failed", __FUNCTION__); + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return (-1); + } + + /* now generate the PORT command */ + adp = (unsigned char *) &((struct sockaddr_in *)&data_address)->sin_addr; + portp = (unsigned char *) &((struct sockaddr_in *)&data_address)->sin_port; + snprintf(buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n", + adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff, + portp[0] & 0xff, portp[1] & 0xff); + + buf[sizeof(buf) - 1] = 0; + len = strlen(buf); + + /* send the PORT command to the server */ + res = send(ctxt->control_file_desc, buf, len, 0); + + if (res < 0) { + motion_log(LOG_ERR, 1, "%s: send failed in ftp_get_connection", __FUNCTION__); + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return(res); + } + + res = ftp_get_response(ctxt); + + if (res != 2) { + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return(-1); + } + } + + return(ctxt->data_file_desc); } /** @@ -630,45 +666,45 @@ static int ftp_get_connection(ftp_context_pointer ctxt) { */ static int ftp_close_connection(ftp_context_pointer ctxt) { - int res; - fd_set rfd, efd; - struct timeval tv; - - if ((ctxt == NULL) || (ctxt->control_file_desc < 0)) - return(-1); - - close(ctxt->data_file_desc); - ctxt->data_file_desc = -1; - - /* Check for data on the control channel */ - tv.tv_sec = 15; - tv.tv_usec = 0; - FD_ZERO(&rfd); - FD_SET(ctxt->control_file_desc, &rfd); - FD_ZERO(&efd); - FD_SET(ctxt->control_file_desc, &efd); - res = select(ctxt->control_file_desc + 1, &rfd, NULL, &efd, &tv); - - if (res < 0) { - close(ctxt->control_file_desc); - ctxt->control_file_desc = -1; - return(-1); - } - - if (res == 0) { /* timeout */ - close(ctxt->control_file_desc); - ctxt->control_file_desc = -1; - } else { /* read the response */ - res = ftp_get_response(ctxt); - - if (res != 2) { /* should be positive completion (2) */ - close(ctxt->control_file_desc); - ctxt->control_file_desc = -1; - return(-1); - } - } - - return(0); + int res; + fd_set rfd, efd; + struct timeval tv; + + if ((ctxt == NULL) || (ctxt->control_file_desc < 0)) + return(-1); + + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + + /* Check for data on the control channel */ + tv.tv_sec = 15; + tv.tv_usec = 0; + FD_ZERO(&rfd); + FD_SET(ctxt->control_file_desc, &rfd); + FD_ZERO(&efd); + FD_SET(ctxt->control_file_desc, &efd); + res = select(ctxt->control_file_desc + 1, &rfd, NULL, &efd, &tv); + + if (res < 0) { + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + return(-1); + } + + if (res == 0) { /* timeout */ + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + } else { /* read the response */ + res = ftp_get_response(ctxt); + + if (res != 2) { /* should be positive completion (2) */ + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + return(-1); + } + } + + return(0); } /** @@ -685,69 +721,69 @@ static int ftp_close_connection(ftp_context_pointer ctxt) { int ftp_get_socket(ftp_context_pointer ctxt) { - char buf[300]; - int res, len; - int acfd; - - if ((ctxt == NULL) || (ctxt->path == NULL)) - return(-1); - - /* Set up the data connection */ - ctxt->data_file_desc = ftp_get_connection(ctxt); - - if (ctxt->data_file_desc == -1) - return(-1); - - /* generate a "retrieve" command for the file */ - snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path); - buf[sizeof(buf) - 1] = 0; - len = strlen(buf); - - /* send it to the server */ - res = send(ctxt->control_file_desc, buf, len, 0); - - if (res < 0) { - motion_log(LOG_ERR, 1, "%s: send failed in ftp_get_socket", __FUNCTION__); - close(ctxt->data_file_desc); - ctxt->data_file_desc = -1; - return(res); - } - - /* check the answer */ - res = ftp_get_response(ctxt); - - if (res != 1) { - close(ctxt->data_file_desc); - ctxt->data_file_desc = -1; - return(-res); - } - - /* - * if not a passive connection, need to do an accept to get the - * connection from the server - */ - if (!ctxt->passive) { - struct sockaddr_in data_address; - unsigned int data_address_length = sizeof(struct sockaddr_in); - - if ((acfd = accept(ctxt->data_file_desc, (struct sockaddr *)&data_address, - &data_address_length)) < 0) { - motion_log(LOG_ERR, 1, "%s: accept in ftp_get_socket", __FUNCTION__); - close(ctxt->data_file_desc); - ctxt->data_file_desc = -1; - return -1; - } - - close(ctxt->data_file_desc); - ctxt->data_file_desc = acfd; - } - - return(ctxt->data_file_desc); + char buf[300]; + int res, len; + int acfd; + + if ((ctxt == NULL) || (ctxt->path == NULL)) + return(-1); + + /* Set up the data connection */ + ctxt->data_file_desc = ftp_get_connection(ctxt); + + if (ctxt->data_file_desc == -1) + return(-1); + + /* generate a "retrieve" command for the file */ + snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path); + buf[sizeof(buf) - 1] = 0; + len = strlen(buf); + + /* send it to the server */ + res = send(ctxt->control_file_desc, buf, len, 0); + + if (res < 0) { + motion_log(LOG_ERR, 1, "%s: send failed in ftp_get_socket", __FUNCTION__); + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return(res); + } + + /* check the answer */ + res = ftp_get_response(ctxt); + + if (res != 1) { + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return(-res); + } + + /* + * if not a passive connection, need to do an accept to get the + * connection from the server + */ + if (!ctxt->passive) { + struct sockaddr_in data_address; + unsigned int data_address_length = sizeof(struct sockaddr_in); + + if ((acfd = accept(ctxt->data_file_desc, (struct sockaddr *)&data_address, + &data_address_length)) < 0) { + motion_log(LOG_ERR, 1, "%s: accept in ftp_get_socket", __FUNCTION__); + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return -1; + } + + close(ctxt->data_file_desc); + ctxt->data_file_desc = acfd; + } + + return(ctxt->data_file_desc); } /** * ftp_send_type * -* Send a TYPE (either 'I' or 'A') command to the server +* Send a TYPE (either 'I' or 'A') command to the server * * Parameters * @@ -758,31 +794,31 @@ int ftp_get_socket(ftp_context_pointer ctxt) { * */ int ftp_send_type(ftp_context_pointer ctxt, char type) { - char buf[100], utype; - int len, res; - - utype = toupper(type); - /* Assure transfer will be in "image" mode */ - snprintf(buf, sizeof(buf), "TYPE I\r\n"); - len = strlen(buf); - res = send(ctxt->control_file_desc, buf, len, 0); - - if (res < 0) { - motion_log(LOG_ERR, 1, "%s: send failed in ftp_get_socket", __FUNCTION__); - close(ctxt->data_file_desc); - ctxt->data_file_desc = -1; - return(res); - } - - res = ftp_get_response(ctxt); - - if (res != 2) { - close(ctxt->data_file_desc); - ctxt->data_file_desc = -1; - return(-res); - } - - return 0; + char buf[100], utype; + int len, res; + + utype = toupper(type); + /* Assure transfer will be in "image" mode */ + snprintf(buf, sizeof(buf), "TYPE I\r\n"); + len = strlen(buf); + res = send(ctxt->control_file_desc, buf, len, 0); + + if (res < 0) { + motion_log(LOG_ERR, 1, "%s: send failed in ftp_get_socket", __FUNCTION__); + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return(res); + } + + res = ftp_get_response(ctxt); + + if (res != 2) { + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + return(-res); + } + + return 0; } /** @@ -801,27 +837,27 @@ int ftp_send_type(ftp_context_pointer ctxt, char type) { * -1 indicates a parameter error. */ int ftp_read(ftp_context_pointer ctxt, void *dest, int len) { - if (ctxt == NULL) - return(-1); + if (ctxt == NULL) + return(-1); - if (ctxt->data_file_desc < 0) - return(0); + if (ctxt->data_file_desc < 0) + return(0); - if (dest == NULL) - return(-1); + if (dest == NULL) + return(-1); - if (len <= 0) - return(0); + if (len <= 0) + return(0); - len = recv(ctxt->data_file_desc, dest, len, 0); + len = recv(ctxt->data_file_desc, dest, len, 0); - if (len <= 0) { - if (len < 0) - motion_log(LOG_ERR, 1, "%s: recv failed in ftp_read", __FUNCTION__); - ftp_close_connection(ctxt); - } + if (len <= 0) { + if (len < 0) + motion_log(LOG_ERR, 1, "%s: recv failed in ftp_read", __FUNCTION__); + ftp_close_connection(ctxt); + } - return(len); + return(len); } @@ -837,20 +873,20 @@ int ftp_read(ftp_context_pointer ctxt, void *dest, int len) { * Returns -1 in case of error, 0 otherwise */ int ftp_close(ftp_context_pointer ctxt) { - if (ctxt == NULL) - return(-1); - - if (ctxt->data_file_desc >= 0) { - close(ctxt->data_file_desc); - ctxt->data_file_desc = -1; - } - - if (ctxt->control_file_desc >= 0) { - ftp_quit(ctxt); - close(ctxt->control_file_desc); - ctxt->control_file_desc = -1; - } - - ftp_free_context(ctxt); - return(0); + if (ctxt == NULL) + return(-1); + + if (ctxt->data_file_desc >= 0) { + close(ctxt->data_file_desc); + ctxt->data_file_desc = -1; + } + + if (ctxt->control_file_desc >= 0) { + ftp_quit(ctxt); + close(ctxt->control_file_desc); + ctxt->control_file_desc = -1; + } + + ftp_free_context(ctxt); + return(0); } diff --git a/netcam_jpeg.c b/netcam_jpeg.c index c727935..db2d054 100644 --- a/netcam_jpeg.c +++ b/netcam_jpeg.c @@ -1,18 +1,18 @@ /* - * netcam_jpeg.c + * netcam_jpeg.c * - * Module for handling JPEG decompression for network cameras. + * Module for handling JPEG decompression for network cameras. * - * This code was inspired by the original module written by - * Jeroen Vreeken and enhanced by several Motion project - * contributors, particularly Angel Carpintero and - * Christopher Price. + * This code was inspired by the original module written by + * Jeroen Vreeken and enhanced by several Motion project + * contributors, particularly Angel Carpintero and + * Christopher Price. * - * Copyright 2005, William M. Brack - * This program is published under the GNU Public license + * Copyright 2005, William M. Brack + * This program is published under the GNU Public license */ -#include "rotate.h" /* already includes motion.h */ +#include "rotate.h" /* already includes motion.h */ #include #include @@ -22,11 +22,11 @@ * pointer to the standard information) */ typedef struct { - struct jpeg_source_mgr pub; - char *data; - int length; - JOCTET *buffer; - boolean start_of_file; + struct jpeg_source_mgr pub; + char *data; + int length; + JOCTET *buffer; + boolean start_of_file; } netcam_source_mgr; typedef netcam_source_mgr *netcam_src_ptr; @@ -47,68 +47,68 @@ static void netcam_error_exit(j_common_ptr); static void netcam_init_source(j_decompress_ptr cinfo) { - /* - * Get our "private" structure from the libjpeg structure - */ - netcam_src_ptr src = (netcam_src_ptr) cinfo->src; - /* - * Set the 'start_of_file' flag in our private structure - * (used by my_fill_input_buffer) - */ - src->start_of_file = TRUE; + /* + * Get our "private" structure from the libjpeg structure + */ + netcam_src_ptr src = (netcam_src_ptr) cinfo->src; + /* + * Set the 'start_of_file' flag in our private structure + * (used by my_fill_input_buffer) + */ + src->start_of_file = TRUE; } static boolean netcam_fill_input_buffer(j_decompress_ptr cinfo) { - netcam_src_ptr src = (netcam_src_ptr) cinfo->src; - size_t nbytes; - - /* - * start_of_file is set any time netcam_init_source has been called - * since the last entry to this routine. This would be the normal - * path when a new image is to be processed. It is assumed that - * this routine will only be called once for the entire image. - * If an unexpected call (with start_of_file FALSE) occurs, the - * routine will insert a "fake" "end of image" marker and return - * to the library to process whatever data remains from the original - * image (the one with errors). - * - * I'm not yet clear on what the result (from the application's - * point of view) will be from this logic. If the application - * expects that a completely new image will be started, this will - * give trouble. - */ - if (src->start_of_file) { - nbytes = src->length; - src->buffer = (JOCTET *) src->data; - } else { - /* Insert a fake EOI marker - as per jpeglib recommendation */ - if (debug_level > CAMERA_VERBOSE) - motion_log(LOG_INFO, 0, "%s: **fake EOI inserted**", __FUNCTION__); - src->buffer[0] = (JOCTET) 0xFF; - src->buffer[1] = (JOCTET) JPEG_EOI; /* 0xD9 */ - nbytes = 2; - } - - src->pub.next_input_byte = src->buffer; - src->pub.bytes_in_buffer = nbytes; - src->start_of_file = FALSE; - - return TRUE; + netcam_src_ptr src = (netcam_src_ptr) cinfo->src; + size_t nbytes; + + /* + * start_of_file is set any time netcam_init_source has been called + * since the last entry to this routine. This would be the normal + * path when a new image is to be processed. It is assumed that + * this routine will only be called once for the entire image. + * If an unexpected call (with start_of_file FALSE) occurs, the + * routine will insert a "fake" "end of image" marker and return + * to the library to process whatever data remains from the original + * image (the one with errors). + * + * I'm not yet clear on what the result (from the application's + * point of view) will be from this logic. If the application + * expects that a completely new image will be started, this will + * give trouble. + */ + if (src->start_of_file) { + nbytes = src->length; + src->buffer = (JOCTET *) src->data; + } else { + /* Insert a fake EOI marker - as per jpeglib recommendation */ + if (debug_level > CAMERA_VERBOSE) + motion_log(LOG_INFO, 0, "%s: **fake EOI inserted**", __FUNCTION__); + src->buffer[0] = (JOCTET) 0xFF; + src->buffer[1] = (JOCTET) JPEG_EOI; /* 0xD9 */ + nbytes = 2; + } + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = nbytes; + src->start_of_file = FALSE; + + return TRUE; } static void netcam_skip_input_data(j_decompress_ptr cinfo, long num_bytes) { - netcam_src_ptr src = (netcam_src_ptr) cinfo->src; - - if (num_bytes > 0) { - while (num_bytes > (long) src->pub.bytes_in_buffer) { - num_bytes -= (long) src->pub.bytes_in_buffer; - (void) netcam_fill_input_buffer (cinfo); - } - src->pub.next_input_byte += (size_t) num_bytes; - src->pub.bytes_in_buffer -= (size_t) num_bytes; - } + netcam_src_ptr src = (netcam_src_ptr) cinfo->src; + + if (num_bytes > 0) { + while (num_bytes > (long) src->pub.bytes_in_buffer) { + num_bytes -= (long) src->pub.bytes_in_buffer; + (void) netcam_fill_input_buffer (cinfo); + } + src->pub.next_input_byte += (size_t) num_bytes; + src->pub.bytes_in_buffer -= (size_t) num_bytes; + } } static void netcam_term_source(j_decompress_ptr cinfo ATTRIBUTE_UNUSED) @@ -118,50 +118,50 @@ static void netcam_term_source(j_decompress_ptr cinfo ATTRIBUTE_UNUSED) /** * netcam_memory_src * - * Routine to setup for fetching data from a netcam buffer, used by the - * JPEG library decompression routine. + * Routine to setup for fetching data from a netcam buffer, used by the + * JPEG library decompression routine. * * Parameters: - * cinfo pointer to the jpeg decompression object - * data pointer to the image data received from a netcam - * length total size of the image + * cinfo pointer to the jpeg decompression object + * data pointer to the image data received from a netcam + * length total size of the image * * Returns: Nothing * */ static void netcam_memory_src(j_decompress_ptr cinfo, char *data, int length) { - netcam_src_ptr src; - - if (cinfo->src == NULL) { - cinfo->src = (struct jpeg_source_mgr *) - (*cinfo->mem->alloc_small) - ((j_common_ptr) cinfo, JPOOL_PERMANENT, - sizeof (netcam_source_mgr)); - } - - src = (netcam_src_ptr)cinfo->src; - src->data = data; - src->length = length; - src->pub.init_source = netcam_init_source; - src->pub.fill_input_buffer = netcam_fill_input_buffer; - src->pub.skip_input_data = netcam_skip_input_data; - src->pub.resync_to_restart = jpeg_resync_to_restart; - src->pub.term_source = netcam_term_source; - src->pub.bytes_in_buffer = 0; - src->pub.next_input_byte = NULL; + netcam_src_ptr src; + + if (cinfo->src == NULL) + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_PERMANENT, + sizeof (netcam_source_mgr)); + + + src = (netcam_src_ptr)cinfo->src; + src->data = data; + src->length = length; + src->pub.init_source = netcam_init_source; + src->pub.fill_input_buffer = netcam_fill_input_buffer; + src->pub.skip_input_data = netcam_skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; + src->pub.term_source = netcam_term_source; + src->pub.bytes_in_buffer = 0; + src->pub.next_input_byte = NULL; } /** * netcam_error_exit * - * Routine to override the libjpeg error exit routine so - * that we can just throw away the bad frame and continue - * with more data from the netcam. + * Routine to override the libjpeg error exit routine so + * that we can just throw away the bad frame and continue + * with more data from the netcam. * * Parameters * - * cinfo pointer to the decompression control structure + * cinfo pointer to the decompression control structure * * Returns: does an (ugly) longjmp to get back to netcam_jpeg * code @@ -169,254 +169,260 @@ static void netcam_memory_src(j_decompress_ptr cinfo, char *data, int length) */ static void netcam_error_exit(j_common_ptr cinfo) { - /* fetch our pre-stored pointer to the netcam context */ - netcam_context_ptr netcam = cinfo->client_data; - /* output the message associated with the error */ - (*cinfo->err->output_message)(cinfo); - /* set flag to show the decompression had errors */ - netcam->jpeg_error |= 1; - /* need to "cleanup" the aborted decompression */ - jpeg_destroy (cinfo); - - if (debug_level > CAMERA_VERBOSE) - motion_log(LOG_ERR, 0, "%s: netcam->jpeg_error %d", __FUNCTION__, netcam->jpeg_error); - - /* jump back to wherever we started */ - longjmp(netcam->setjmp_buffer, 1); + /* fetch our pre-stored pointer to the netcam context */ + netcam_context_ptr netcam = cinfo->client_data; + /* output the message associated with the error */ + (*cinfo->err->output_message)(cinfo); + /* set flag to show the decompression had errors */ + netcam->jpeg_error |= 1; + /* need to "cleanup" the aborted decompression */ + jpeg_destroy (cinfo); + + if (debug_level > CAMERA_VERBOSE) + motion_log(LOG_ERR, 0, "%s: netcam->jpeg_error %d", __FUNCTION__, + netcam->jpeg_error); + + /* jump back to wherever we started */ + longjmp(netcam->setjmp_buffer, 1); } /** * netcam_output_message * - * Routine to override the libjpeg error message output routine. - * We do this so that we can output our module and thread i.d., - * as well as put the message to the motion log. + * Routine to override the libjpeg error message output routine. + * We do this so that we can output our module and thread i.d., + * as well as put the message to the motion log. * * Parameters * - * cinfo pointer to the decompression control structure + * cinfo pointer to the decompression control structure * * Returns Nothing * */ static void netcam_output_message(j_common_ptr cinfo) { - char buffer[JMSG_LENGTH_MAX]; - - /* fetch our pre-stored pointer to the netcam context */ - netcam_context_ptr netcam = cinfo->client_data; - - /* - * While experimenting with a "appro" netcam it was discovered - * that the jpeg data produced by the camera caused warning - * messages from libjpeg (JWRN_EXTRANEOUS_DATA). The following - * code is to assure that specific warning is ignored. - * - * NOTE: It's likely that we will discover other error message - * codes which we want to ignore. In that case, we should have - * some sort of table-lookup to decide which messages we really - * care about. - */ - if ( (cinfo->err->msg_code != JWRN_EXTRANEOUS_DATA) && - (cinfo->err->msg_code == JWRN_NOT_SEQUENTIAL) && (!netcam->netcam_broken) ) - netcam->jpeg_error |= 2; /* Set flag to show problem */ - /* - * We only display and log errors when debug_level - * is non-zero. The reasoning here is that these kinds - * of errors are only produced when the input data is - * wrong, and that indicates a network problem rather - * than a problem with the content. - */ - if (debug_level > CAMERA_VERBOSE) { - /* - * Format the message according to library standards. - * Write it out to the motion log. - */ - (*cinfo->err->format_message)(cinfo, buffer); - motion_log(LOG_ERR, 0, "%s: %s", __FUNCTION__, buffer); - } + char buffer[JMSG_LENGTH_MAX]; + + /* fetch our pre-stored pointer to the netcam context */ + netcam_context_ptr netcam = cinfo->client_data; + + /* + * While experimenting with a "appro" netcam it was discovered + * that the jpeg data produced by the camera caused warning + * messages from libjpeg (JWRN_EXTRANEOUS_DATA). The following + * code is to assure that specific warning is ignored. + * + * NOTE: It's likely that we will discover other error message + * codes which we want to ignore. In that case, we should have + * some sort of table-lookup to decide which messages we really + * care about. + */ + if ((cinfo->err->msg_code != JWRN_EXTRANEOUS_DATA) && + (cinfo->err->msg_code == JWRN_NOT_SEQUENTIAL) && (!netcam->netcam_broken)) + netcam->jpeg_error |= 2; /* Set flag to show problem */ + /* + * We only display and log errors when debug_level + * is non-zero. The reasoning here is that these kinds + * of errors are only produced when the input data is + * wrong, and that indicates a network problem rather + * than a problem with the content. + */ + if (debug_level > CAMERA_VERBOSE) { + /* + * Format the message according to library standards. + * Write it out to the motion log. + */ + (*cinfo->err->format_message)(cinfo, buffer); + motion_log(LOG_ERR, 0, "%s: %s", __FUNCTION__, buffer); + } } /** * netcam_init_jpeg * - * Initialises the JPEG library prior to doing a - * decompression. + * Initialises the JPEG library prior to doing a + * decompression. * * Parameters: - * netcam pointer to netcam_context - * cinfo pointer to JPEG decompression context + * netcam pointer to netcam_context + * cinfo pointer to JPEG decompression context * * Returns: Error code */ static int netcam_init_jpeg(netcam_context_ptr netcam, j_decompress_ptr cinfo) { - netcam_buff_ptr buff; - - /* - * First we check whether a new image has arrived. If not, we - * setup to wait for 1/2 a frame time. This will (hopefully) - * help in synchronizing the camera frames with the motion main - * loop. - */ - pthread_mutex_lock(&netcam->mutex); - - if (netcam->imgcnt_last == netcam->imgcnt) { /* need to wait */ - struct timespec waittime; - struct timeval curtime; - int retcode; - - /* - * We calculate the delay time (representing the desired frame - * rate). This delay time is in *nanoseconds*. - * We will wait 0.5 seconds which gives a practical minimum - * framerate of 2 which is desired for the motion_loop to - * function. - */ - gettimeofday(&curtime, NULL); - curtime.tv_usec += 500000; - - if (curtime.tv_usec > 1000000) { - curtime.tv_usec -= 1000000; - curtime.tv_sec++; - } - - waittime.tv_sec = curtime.tv_sec; - waittime.tv_nsec = 1000L * curtime.tv_usec; - - do { - retcode = pthread_cond_timedwait(&netcam->pic_ready, - &netcam->mutex, &waittime); - } while (retcode == EINTR); - - if (retcode) { /* we assume a non-zero reply is ETIMEOUT */ - pthread_mutex_unlock(&netcam->mutex); - - if (debug_level > CAMERA_WARNINGS) - motion_log(-1, 0, "%s: no new pic, no signal rcvd", __FUNCTION__); - - return NETCAM_GENERAL_ERROR | NETCAM_NOTHING_NEW_ERROR; - } - - if (debug_level > CAMERA_VERBOSE) - motion_log(-1, 0, "%s: ***new pic delay successful***", __FUNCTION__); - } - - netcam->imgcnt_last = netcam->imgcnt; - - /* set latest buffer as "current" */ - buff = netcam->latest; - netcam->latest = netcam->jpegbuf; - netcam->jpegbuf = buff; - pthread_mutex_unlock(&netcam->mutex); - - /* clear any error flag from previous work */ - netcam->jpeg_error = 0; - - buff = netcam->jpegbuf; - /* prepare for the decompression */ - /* Initialize the JPEG decompression object */ - jpeg_create_decompress(cinfo); - - /* Set up own error exit routine */ - cinfo->err = jpeg_std_error(&netcam->jerr); - cinfo->client_data = netcam; - netcam->jerr.error_exit = netcam_error_exit; - netcam->jerr.output_message = netcam_output_message; - - /* Specify the data source as our own routine */ - netcam_memory_src(cinfo, buff->ptr, buff->used); - - /* Read file parameters (rejecting tables-only) */ - jpeg_read_header(cinfo, TRUE); - - /* Override the desired colour space */ - cinfo->out_color_space = JCS_YCbCr; - - /* Start the decompressor */ - jpeg_start_decompress(cinfo); - - if (debug_level > CAMERA_VERBOSE) - motion_log(LOG_INFO, 0, "%s: jpeg_error %d", __FUNCTION__, netcam->jpeg_error); - - return netcam->jpeg_error; + netcam_buff_ptr buff; + + /* + * First we check whether a new image has arrived. If not, we + * setup to wait for 1/2 a frame time. This will (hopefully) + * help in synchronizing the camera frames with the motion main + * loop. + */ + pthread_mutex_lock(&netcam->mutex); + + if (netcam->imgcnt_last == netcam->imgcnt) { /* need to wait */ + struct timespec waittime; + struct timeval curtime; + int retcode; + + /* + * We calculate the delay time (representing the desired frame + * rate). This delay time is in *nanoseconds*. + * We will wait 0.5 seconds which gives a practical minimum + * framerate of 2 which is desired for the motion_loop to + * function. + */ + gettimeofday(&curtime, NULL); + curtime.tv_usec += 500000; + + if (curtime.tv_usec > 1000000) { + curtime.tv_usec -= 1000000; + curtime.tv_sec++; + } + + waittime.tv_sec = curtime.tv_sec; + waittime.tv_nsec = 1000L * curtime.tv_usec; + + do { + retcode = pthread_cond_timedwait(&netcam->pic_ready, + &netcam->mutex, &waittime); + } while (retcode == EINTR); + + if (retcode) { /* we assume a non-zero reply is ETIMEOUT */ + pthread_mutex_unlock(&netcam->mutex); + + if (debug_level > CAMERA_WARNINGS) + motion_log(-1, 0, "%s: no new pic, no signal rcvd", __FUNCTION__); + + return NETCAM_GENERAL_ERROR | NETCAM_NOTHING_NEW_ERROR; + } + + if (debug_level > CAMERA_VERBOSE) + motion_log(-1, 0, "%s: ***new pic delay successful***", __FUNCTION__); + } + + netcam->imgcnt_last = netcam->imgcnt; + + /* set latest buffer as "current" */ + buff = netcam->latest; + netcam->latest = netcam->jpegbuf; + netcam->jpegbuf = buff; + pthread_mutex_unlock(&netcam->mutex); + + /* clear any error flag from previous work */ + netcam->jpeg_error = 0; + + buff = netcam->jpegbuf; + /* prepare for the decompression */ + /* Initialize the JPEG decompression object */ + jpeg_create_decompress(cinfo); + + /* Set up own error exit routine */ + cinfo->err = jpeg_std_error(&netcam->jerr); + cinfo->client_data = netcam; + netcam->jerr.error_exit = netcam_error_exit; + netcam->jerr.output_message = netcam_output_message; + + /* Specify the data source as our own routine */ + netcam_memory_src(cinfo, buff->ptr, buff->used); + + /* Read file parameters (rejecting tables-only) */ + jpeg_read_header(cinfo, TRUE); + + /* Override the desired colour space */ + cinfo->out_color_space = JCS_YCbCr; + + /* Start the decompressor */ + jpeg_start_decompress(cinfo); + + if (debug_level > CAMERA_VERBOSE) + motion_log(LOG_INFO, 0, "%s: jpeg_error %d", __FUNCTION__, netcam->jpeg_error); + + return netcam->jpeg_error; } static int netcam_image_conv(netcam_context_ptr netcam, struct jpeg_decompress_struct *cinfo, unsigned char *image) { - JSAMPARRAY line; /* array of decomp data lines */ - unsigned char *wline; /* will point to line[0] */ + JSAMPARRAY line; /* array of decomp data lines */ + unsigned char *wline; /* will point to line[0] */ /* Working variables */ - int linesize, i; - unsigned char *upic, *vpic; - unsigned char *pic = image; - unsigned char y; /* switch for decoding YUV data */ - unsigned int width, height; - - width = cinfo->output_width; - height = cinfo->output_height; - if (width && ((width != netcam->width) || (height != netcam->height))) { - motion_log(LOG_ERR, 0, - "%s: JPEG image size %dx%d, JPEG was %dx%d", - __FUNCTION__, netcam->width, netcam->height, width, height); - jpeg_destroy_decompress (cinfo); - netcam->jpeg_error |= 4; - return netcam->jpeg_error; - } - /* Set the output pointers (these come from YUV411P definition */ - upic = pic + width * height; - vpic = upic + (width * height) / 4; - - - /* YCbCr format will give us one byte each for YUV */ - linesize = cinfo->output_width * 3; - - /* Allocate space for one line */ - line = (cinfo->mem->alloc_sarray)((j_common_ptr) cinfo, JPOOL_IMAGE, - cinfo->output_width * cinfo->output_components, 1); - - wline = line[0]; - y = 0; - while (cinfo->output_scanline < height) { - jpeg_read_scanlines (cinfo, line, 1); - - for (i = 0; i < linesize; i += 3) { - pic[i / 3] = wline[i]; - if (i & 1) { - upic[(i / 3) / 2] = wline[i + 1]; - vpic[(i / 3) / 2] = wline[i + 2]; - } - } - pic += linesize / 3; - if (y++ & 1) { - upic += width / 2; - vpic += width / 2; - } - } - - jpeg_finish_decompress (cinfo); - jpeg_destroy_decompress (cinfo); - if (netcam->cnt->rotate_data.degrees > 0) { - /* rotate as specified */ - rotate_map(netcam->cnt, image); - } - - if (debug_level > CAMERA_VERBOSE) - motion_log(LOG_INFO, 0, "%s: jpeg_error %d", __FUNCTION__, netcam->jpeg_error); + int linesize, i; + unsigned char *upic, *vpic; + unsigned char *pic = image; + unsigned char y; /* switch for decoding YUV data */ + unsigned int width, height; + + width = cinfo->output_width; + height = cinfo->output_height; + + if (width && ((width != netcam->width) || (height != netcam->height))) { + motion_log(LOG_ERR, 0, + "%s: JPEG image size %dx%d, JPEG was %dx%d", + __FUNCTION__, netcam->width, netcam->height, width, height); + jpeg_destroy_decompress (cinfo); + netcam->jpeg_error |= 4; + return netcam->jpeg_error; + } + /* Set the output pointers (these come from YUV411P definition */ + upic = pic + width * height; + vpic = upic + (width * height) / 4; + + + /* YCbCr format will give us one byte each for YUV */ + linesize = cinfo->output_width * 3; + + /* Allocate space for one line */ + line = (cinfo->mem->alloc_sarray)((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->output_width * cinfo->output_components, 1); + + wline = line[0]; + y = 0; + + while (cinfo->output_scanline < height) { + jpeg_read_scanlines (cinfo, line, 1); + + for (i = 0; i < linesize; i += 3) { + pic[i / 3] = wline[i]; + if (i & 1) { + upic[(i / 3) / 2] = wline[i + 1]; + vpic[(i / 3) / 2] = wline[i + 2]; + } + } + + pic += linesize / 3; + + if (y++ & 1) { + upic += width / 2; + vpic += width / 2; + } + } + + jpeg_finish_decompress (cinfo); + jpeg_destroy_decompress (cinfo); + + if (netcam->cnt->rotate_data.degrees > 0) + /* rotate as specified */ + rotate_map(netcam->cnt, image); + + if (debug_level > CAMERA_VERBOSE) + motion_log(LOG_INFO, 0, "%s: jpeg_error %d", __FUNCTION__, + netcam->jpeg_error); - return netcam->jpeg_error; + return netcam->jpeg_error; } /** * netcam_proc_jpeg * - * Routine to decode an image received from a netcam into a YUV420P buffer - * suitable for processing by motion. + * Routine to decode an image received from a netcam into a YUV420P buffer + * suitable for processing by motion. * * Parameters: - * netcam pointer to the netcam_context structure + * netcam pointer to the netcam_context structure * image Pointer to a buffer for the returned image * * Returns: @@ -428,87 +434,85 @@ static int netcam_image_conv(netcam_context_ptr netcam, */ int netcam_proc_jpeg(netcam_context_ptr netcam, unsigned char *image) { - struct jpeg_decompress_struct cinfo; /* decompression control struct */ - int retval = 0; /* value returned to caller */ - int ret; /* working var */ - - /* - * This routine is only called from the main thread. - * We need to "protect" the "latest" image while we - * decompress it. netcam_init_jpeg uses - * netcam->mutex to do this; - */ - if (debug_level > CAMERA_INFO) { - motion_log(LOG_INFO, 0, "%s: processing jpeg image - content length " - "%d", __FUNCTION__, netcam->latest->content_length); - } - - ret = netcam_init_jpeg(netcam, &cinfo); - - if (ret != 0) { - if (debug_level > CAMERA_INFO) - motion_log(LOG_ERR, 0, "%s: ret %d", __FUNCTION__, ret); - return ret; - } - - /* Do a sanity check on dimensions - * If dimensions have changed we throw an - * error message that will cause - * restart of Motion - */ - if (netcam->width) { /* 0 means not yet init'ed */ - if ((cinfo.output_width != netcam->width) || - (cinfo.output_height != netcam->height)) { - retval = NETCAM_RESTART_ERROR; - motion_log(LOG_ERR, 0, - "%s: Camera width/height mismatch " - "with JPEG image - expected %dx%d, JPEG %dx%d", - " retval %d", __FUNCTION__, netcam->width, netcam->height, - cinfo.output_width, cinfo.output_height, retval); - return retval; - } - } - - /* do the conversion */ - ret = netcam_image_conv(netcam, &cinfo, image); - - if (ret != 0) { - retval |= NETCAM_JPEG_CONV_ERROR; - if (debug_level > CAMERA_INFO) - motion_log(LOG_ERR, 0, "%s: ret %d retval %d", __FUNCTION__, - ret, retval); - } - - return retval; + struct jpeg_decompress_struct cinfo; /* decompression control struct */ + int retval = 0; /* value returned to caller */ + int ret; /* working var */ + + /* + * This routine is only called from the main thread. + * We need to "protect" the "latest" image while we + * decompress it. netcam_init_jpeg uses + * netcam->mutex to do this; + */ + if (debug_level > CAMERA_INFO) + motion_log(LOG_INFO, 0, "%s: processing jpeg image - content length " + "%d", __FUNCTION__, netcam->latest->content_length); + + ret = netcam_init_jpeg(netcam, &cinfo); + + if (ret != 0) { + if (debug_level > CAMERA_INFO) + motion_log(LOG_ERR, 0, "%s: ret %d", __FUNCTION__, ret); + return ret; + } + + /* Do a sanity check on dimensions + * If dimensions have changed we throw an + * error message that will cause + * restart of Motion + */ + if (netcam->width) { /* 0 means not yet init'ed */ + if ((cinfo.output_width != netcam->width) || + (cinfo.output_height != netcam->height)) { + retval = NETCAM_RESTART_ERROR; + motion_log(LOG_ERR, 0, "%s: Camera width/height mismatch " + "with JPEG image - expected %dx%d, JPEG %dx%d", + " retval %d", __FUNCTION__, netcam->width, netcam->height, + cinfo.output_width, cinfo.output_height, retval); + return retval; + } + } + + /* do the conversion */ + ret = netcam_image_conv(netcam, &cinfo, image); + + if (ret != 0) { + retval |= NETCAM_JPEG_CONV_ERROR; + if (debug_level > CAMERA_INFO) + motion_log(LOG_ERR, 0, "%s: ret %d retval %d", __FUNCTION__, + ret, retval); + } + + return retval; } /** * netcam_get_dimensions * - * This function gets the height and width of the JPEG image - * located in the supplied netcam_image_buffer + * This function gets the height and width of the JPEG image + * located in the supplied netcam_image_buffer * * Parameters * - * netcam pointer to the netcam context + * netcam pointer to the netcam context * * Returns: Nothing, but fills in width and height into context * */ void netcam_get_dimensions(netcam_context_ptr netcam) { - struct jpeg_decompress_struct cinfo; /* decompression control struct */ - int ret; + struct jpeg_decompress_struct cinfo; /* decompression control struct */ + int ret; - ret = netcam_init_jpeg(netcam, &cinfo); + ret = netcam_init_jpeg(netcam, &cinfo); - netcam->width = cinfo.output_width; - netcam->height = cinfo.output_height; - netcam->JFIF_marker = cinfo.saw_JFIF_marker; + netcam->width = cinfo.output_width; + netcam->height = cinfo.output_height; + netcam->JFIF_marker = cinfo.saw_JFIF_marker; - jpeg_destroy_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); - if (debug_level > CAMERA_INFO) - motion_log(LOG_ERR, 0, "%s: JFIF_marker %s PRESENT ret %d", - __FUNCTION__, netcam->JFIF_marker ? "IS" : "NOT", ret); + if (debug_level > CAMERA_INFO) + motion_log(LOG_ERR, 0, "%s: JFIF_marker %s PRESENT ret %d", + __FUNCTION__, netcam->JFIF_marker ? "IS" : "NOT", ret); } diff --git a/netcam_wget.c b/netcam_wget.c index 32a893c..cdb67cb 100644 --- a/netcam_wget.c +++ b/netcam_wget.c @@ -1,6 +1,6 @@ /* Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001, 2002 - Free Software Foundation, Inc. + Free Software Foundation, Inc. Additional Copyright (C) 2004-2005 Christopher Price, Angel Carpintero, and other contributing authors. @@ -38,14 +38,14 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ definition is not HTTP-specific -- it is virtually indistinguishable from the one given in RFC822 or RFC1036. - message-header = field-name ":" [ field-value ] CRLF + message-header = field-name ":" [ field-value ] CRLF - field-name = token - field-value = *( field-content | LWS ) + field-name = token + field-value = *( field-content | LWS ) - field-content = + field-content = The public functions are header_get() and header_process(), which see. */ @@ -67,78 +67,78 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ int header_get(netcam_context_ptr netcam, char **hdr, enum header_get_flags flags) { - int i; - int bufsize = 80; - - *hdr = (char *)mymalloc(bufsize); - for (i = 0; 1; i++) { - int res; - /* #### Use DO_REALLOC? */ - if (i > bufsize - 1) - *hdr = (char *)myrealloc(*hdr, (bufsize <<= 1), ""); - - res = RBUF_READCHAR (netcam, *hdr + i); - - if (res == 1) { - if ((*hdr)[i] == '\n') { - if (!((flags & HG_NO_CONTINUATIONS) || i == 0 - || (i == 1 && (*hdr)[0] == '\r'))) { - char next; - /* If the header is non-empty, we need to check if - it continues on to the other line. We do that by - peeking at the next character. */ - res = rbuf_peek(netcam, &next); - - if (res == 0) { - (*hdr)[i] = '\0'; - return HG_EOF; - } else if (res == -1) { - (*hdr)[i] = '\0'; - return HG_ERROR; - } - /* If the next character is HT or SP, just continue. */ - - if (next == '\t' || next == ' ') - continue; - } - - /* Strip trailing whitespace. (*hdr)[i] is the newline; - decrement I until it points to the last available - whitespace. */ - while (i > 0 && isspace((*hdr)[i - 1])) - --i; - - (*hdr)[i] = '\0'; - break; - } - } else if (res == 0) { - (*hdr)[i] = '\0'; - return HG_EOF; - } else { - (*hdr)[i] = '\0'; - return HG_ERROR; - } - } - - return HG_OK; + int i; + int bufsize = 80; + + *hdr = (char *)mymalloc(bufsize); + + for (i = 0; 1; i++) { + int res; + /* #### Use DO_REALLOC? */ + if (i > bufsize - 1) + *hdr = (char *)myrealloc(*hdr, (bufsize <<= 1), ""); + + res = RBUF_READCHAR (netcam, *hdr + i); + + if (res == 1) { + if ((*hdr)[i] == '\n') { + if (!((flags & HG_NO_CONTINUATIONS) || i == 0 + || (i == 1 && (*hdr)[0] == '\r'))) { + char next; + /* If the header is non-empty, we need to check if + it continues on to the other line. We do that by + peeking at the next character. */ + res = rbuf_peek(netcam, &next); + + if (res == 0) { + (*hdr)[i] = '\0'; + return HG_EOF; + } else if (res == -1) { + (*hdr)[i] = '\0'; + return HG_ERROR; + } + /* If the next character is HT or SP, just continue. */ + + if (next == '\t' || next == ' ') + continue; + } + + /* Strip trailing whitespace. (*hdr)[i] is the newline; + decrement I until it points to the last available + whitespace. */ + while (i > 0 && isspace((*hdr)[i - 1])) + --i; + + (*hdr)[i] = '\0'; + break; + } + } else if (res == 0) { + (*hdr)[i] = '\0'; + return HG_EOF; + } else { + (*hdr)[i] = '\0'; + return HG_ERROR; + } + } + + return HG_OK; } /* Check whether HEADER begins with NAME and, if yes, skip the `:' and the whitespace, and call PROCFUN with the arguments of HEADER's contents (after the `:' and space) and ARG. Otherwise, return 0. */ int header_process (const char *header, const char *name, - int (*procfun)(const char *, void *), - void *arg) + int (*procfun)(const char *, void *), void *arg) { - /* Check whether HEADER matches NAME. */ - while (*name && (tolower (*name) == tolower (*header))) - ++name, ++header; - - if (*name || *header++ != ':') - return 0; - - header += skip_lws (header); - return ((*procfun) (header, arg)); + /* Check whether HEADER matches NAME. */ + while (*name && (tolower (*name) == tolower (*header))) + ++name, ++header; + + if (*name || *header++ != ':') + return 0; + + header += skip_lws (header); + return ((*procfun) (header, arg)); } /* Helper functions for use with header_process(). */ @@ -147,34 +147,34 @@ int header_process (const char *header, const char *name, error is encountered, return 0, else 1. */ int header_extract_number(const char *header, void *closure) { - const char *p = header; - long result; + const char *p = header; + long result; - for (result = 0; isdigit (*p); p++) - result = 10 * result + (*p - '0'); + for (result = 0; isdigit (*p); p++) + result = 10 * result + (*p - '0'); - /* Failure if no number present. */ - if (p == header) - return 0; + /* Failure if no number present. */ + if (p == header) + return 0; - /* Skip trailing whitespace. */ - p += skip_lws (p); + /* Skip trailing whitespace. */ + p += skip_lws (p); - /* We return the value, even if a format error follows */ - *(long *)closure = result; + /* We return the value, even if a format error follows */ + *(long *)closure = result; - /* Indicate failure if trailing garbage is present. */ - if (*p) - return 0; + /* Indicate failure if trailing garbage is present. */ + if (*p) + return 0; - return 1; + return 1; } /* Strdup HEADER, and place the pointer to CLOSURE. */ int header_strdup(const char *header, void *closure) { - *(char **)closure = mystrdup(header); - return 1; + *(char **)closure = mystrdup(header); + return 1; } @@ -182,148 +182,148 @@ int header_strdup(const char *header, void *closure) characters to skip. */ int skip_lws(const char *string) { - const char *p = string; + const char *p = string; - while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') - ++p; + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') + ++p; - return p - string; + return p - string; } /* - Encode the string S of length LENGTH to base64 format and place it - to STORE. STORE will be 0-terminated, and must point to a writable - buffer of at least 1+BASE64_LENGTH(length) bytes. + Encode the string S of length LENGTH to base64 format and place it + to STORE. STORE will be 0-terminated, and must point to a writable + buffer of at least 1+BASE64_LENGTH(length) bytes. */ void base64_encode(const char *s, char *store, int length) { - /* Conversion table. */ - static const char tbl[64] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '+', '/' - }; - - int i; - unsigned char *p = (unsigned char *)store; - - /* Transform the 3x8 bits to 4x6 bits, as required by base64. */ - for (i = 0; i < length; i += 3) { - *p++ = tbl[s[0] >> 2]; - *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)]; - *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)]; - *p++ = tbl[s[2] & 0x3f]; - s += 3; - } - - /* Pad the result if necessary... */ - if (i == length + 1) - *(p - 1) = '='; - else if (i == length + 2) - *(p - 1) = *(p - 2) = '='; - - /* ...and zero-terminate it. */ - *p = '\0'; + /* Conversion table. */ + static const char tbl[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' + }; + + int i; + unsigned char *p = (unsigned char *)store; + + /* Transform the 3x8 bits to 4x6 bits, as required by base64. */ + for (i = 0; i < length; i += 3) { + *p++ = tbl[s[0] >> 2]; + *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)]; + *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)]; + *p++ = tbl[s[2] & 0x3f]; + s += 3; + } + + /* Pad the result if necessary... */ + if (i == length + 1) + *(p - 1) = '='; + else if (i == length + 2) + *(p - 1) = *(p - 2) = '='; + + /* ...and zero-terminate it. */ + *p = '\0'; } char *strdupdelim(const char *beg, const char *end) { - char *res = (char *)mymalloc(end - beg + 1); - memcpy (res, beg, end - beg); + char *res = (char *)mymalloc(end - beg + 1); + memcpy (res, beg, end - beg); - res[end - beg] = '\0'; - return res; + res[end - beg] = '\0'; + return res; } int http_process_type(const char *hdr, void *arg) { - char **result = (char **)arg; - /* Locate P on `;' or the terminating zero, whichever comes first. */ - const char *p = strchr (hdr, ';'); + char **result = (char **)arg; + /* Locate P on `;' or the terminating zero, whichever comes first. */ + const char *p = strchr (hdr, ';'); - if (!p) - p = hdr + strlen (hdr); + if (!p) + p = hdr + strlen (hdr); - while (p > hdr && isspace (*(p - 1))) - --p; + while (p > hdr && isspace (*(p - 1))) + --p; - *result = strdupdelim (hdr, p); - return 1; + *result = strdupdelim (hdr, p); + return 1; } /* This is a simple implementation of buffering IO-read functions. */ void rbuf_initialize(netcam_context_ptr netcam) { - netcam->response->buffer_pos = netcam->response->buffer; - netcam->response->buffer_left = 0; + netcam->response->buffer_pos = netcam->response->buffer; + netcam->response->buffer_left = 0; } int rbuf_read_bufferful(netcam_context_ptr netcam) { - return netcam_recv(netcam, netcam->response->buffer, - sizeof (netcam->response->buffer)); + return netcam_recv(netcam, netcam->response->buffer, + sizeof (netcam->response->buffer)); } /* Like rbuf_readchar(), only don't move the buffer position. */ int rbuf_peek(netcam_context_ptr netcam, char *store) { - if (!netcam->response->buffer_left) { - int res; - rbuf_initialize(netcam); - res = netcam_recv (netcam, netcam->response->buffer, - sizeof (netcam->response->buffer)); - - if (res <= 0) - return res; - - netcam->response->buffer_left = res; - } - - *store = *netcam->response->buffer_pos; - return 1; + if (!netcam->response->buffer_left) { + int res; + rbuf_initialize(netcam); + res = netcam_recv (netcam, netcam->response->buffer, + sizeof (netcam->response->buffer)); + + if (res <= 0) + return res; + + netcam->response->buffer_left = res; + } + + *store = *netcam->response->buffer_pos; + return 1; } /* - Flush RBUF's buffer to WHERE. Flush MAXSIZE bytes at most. - Returns the number of bytes actually copied. If the buffer is - empty, 0 is returned. + Flush RBUF's buffer to WHERE. Flush MAXSIZE bytes at most. + Returns the number of bytes actually copied. If the buffer is + empty, 0 is returned. */ int rbuf_flush(netcam_context_ptr netcam, char *where, int maxsize) { - if (!netcam->response->buffer_left) { - return 0; - } else { - int howmuch = MINVAL ((int)netcam->response->buffer_left, maxsize); - - if (where) - memcpy(where, netcam->response->buffer_pos, howmuch); - - netcam->response->buffer_left -= howmuch; - netcam->response->buffer_pos += howmuch; - return howmuch; - } + if (!netcam->response->buffer_left) { + return 0; + } else { + int howmuch = MINVAL ((int)netcam->response->buffer_left, maxsize); + + if (where) + memcpy(where, netcam->response->buffer_pos, howmuch); + + netcam->response->buffer_left -= howmuch; + netcam->response->buffer_pos += howmuch; + return howmuch; + } } /* Get the HTTP result code */ int http_result_code(const char *header) { - char *cptr; + char *cptr; - /* assure the header starts out right */ - if (strncmp(header, "HTTP", 4)) - return -1; + /* assure the header starts out right */ + if (strncmp(header, "HTTP", 4)) + return -1; - /* find the space following the HTTP/1.x */ - if ((cptr = strchr(header+4, ' ')) == NULL) - return -1; + /* find the space following the HTTP/1.x */ + if ((cptr = strchr(header+4, ' ')) == NULL) + return -1; - return atoi(cptr + 1); + return atoi(cptr + 1); } diff --git a/picture.c b/picture.c index efc182c..b8ccdae 100644 --- a/picture.c +++ b/picture.c @@ -1,10 +1,10 @@ -/* picture.c +/* picture.c * - * Various funtions for saving/loading pictures. - * Copyright 2002 by Jeroen Vreeken (pe1rxq@amsat.org) - * Portions of this file are Copyright by Lionnel Maugis - * This software is distributed under the GNU public license version 2 - * See also the file 'COPYING'. + * Various funtions for saving/loading pictures. + * Copyright 2002 by Jeroen Vreeken (pe1rxq@amsat.org) + * Portions of this file are Copyright by Lionnel Maugis + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. * */ @@ -19,10 +19,10 @@ * functions used by put_jpeg_grey_memory and put_jpeg_yuv420p_memory */ typedef struct { - struct jpeg_destination_mgr pub; - JOCTET *buf; - size_t bufsize; - size_t jpegsize; + struct jpeg_destination_mgr pub; + JOCTET *buf; + size_t bufsize; + size_t jpegsize; } mem_destination_mgr; typedef mem_destination_mgr *mem_dest_ptr; @@ -30,53 +30,53 @@ typedef mem_destination_mgr *mem_dest_ptr; METHODDEF(void) init_destination(j_compress_ptr cinfo) { - mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; - dest->pub.next_output_byte = dest->buf; - dest->pub.free_in_buffer = dest->bufsize; - dest->jpegsize = 0; + mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; + dest->pub.next_output_byte = dest->buf; + dest->pub.free_in_buffer = dest->bufsize; + dest->jpegsize = 0; } METHODDEF(boolean) empty_output_buffer(j_compress_ptr cinfo) { - mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; - dest->pub.next_output_byte = dest->buf; - dest->pub.free_in_buffer = dest->bufsize; + mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; + dest->pub.next_output_byte = dest->buf; + dest->pub.free_in_buffer = dest->bufsize; - return FALSE; - ERREXIT(cinfo, JERR_BUFFER_SIZE); + return FALSE; + ERREXIT(cinfo, JERR_BUFFER_SIZE); } METHODDEF(void) term_destination(j_compress_ptr cinfo) { - mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; - dest->jpegsize = dest->bufsize - dest->pub.free_in_buffer; + mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; + dest->jpegsize = dest->bufsize - dest->pub.free_in_buffer; } static GLOBAL(void) jpeg_mem_dest(j_compress_ptr cinfo, JOCTET* buf, size_t bufsize) { - mem_dest_ptr dest; + mem_dest_ptr dest; - if (cinfo->dest == NULL) { - cinfo->dest = (struct jpeg_destination_mgr *) - (*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT, - sizeof(mem_destination_mgr)); - } + if (cinfo->dest == NULL) { + cinfo->dest = (struct jpeg_destination_mgr *) + (*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT, + sizeof(mem_destination_mgr)); + } - dest = (mem_dest_ptr) cinfo->dest; + dest = (mem_dest_ptr) cinfo->dest; - dest->pub.init_destination = init_destination; - dest->pub.empty_output_buffer = empty_output_buffer; - dest->pub.term_destination = term_destination; + dest->pub.init_destination = init_destination; + dest->pub.empty_output_buffer = empty_output_buffer; + dest->pub.term_destination = term_destination; - dest->buf = buf; - dest->bufsize = bufsize; - dest->jpegsize = 0; + dest->buf = buf; + dest->bufsize = bufsize; + dest->jpegsize = 0; } static GLOBAL(int) jpeg_mem_size(j_compress_ptr cinfo) { - mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; - return dest->jpegsize; + mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; + return dest->jpegsize; } @@ -94,59 +94,60 @@ static GLOBAL(int) jpeg_mem_size(j_compress_ptr cinfo) static int put_jpeg_yuv420p_memory(unsigned char *dest_image, int image_size, unsigned char *input_image, int width, int height, int quality) { - int i, j, jpeg_image_size; - - JSAMPROW y[16],cb[16],cr[16]; // y[2][5] = color sample of row 2 and pixel column 5; (one plane) - JSAMPARRAY data[3]; // t[0][2][5] = color sample 0 of row 2 and column 5 - - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; - - data[0] = y; - data[1] = cb; - data[2] = cr; - - cinfo.err = jpeg_std_error(&jerr); // errors get written to stderr - - jpeg_create_compress(&cinfo); - cinfo.image_width = width; - cinfo.image_height = height; - cinfo.input_components = 3; - jpeg_set_defaults (&cinfo); - - jpeg_set_colorspace(&cinfo, JCS_YCbCr); - - cinfo.raw_data_in = TRUE; // supply downsampled data - cinfo.comp_info[0].h_samp_factor = 2; - cinfo.comp_info[0].v_samp_factor = 2; - cinfo.comp_info[1].h_samp_factor = 1; - cinfo.comp_info[1].v_samp_factor = 1; - cinfo.comp_info[2].h_samp_factor = 1; - cinfo.comp_info[2].v_samp_factor = 1; - - jpeg_set_quality(&cinfo, quality, TRUE); - cinfo.dct_method = JDCT_FASTEST; - - jpeg_mem_dest(&cinfo, dest_image, image_size); // data written to mem - - jpeg_start_compress (&cinfo, TRUE); - - for (j = 0; j < height; j += 16) { - for (i = 0; i < 16; i++) { - y[i] = input_image + width*(i+j); - if (i%2 == 0) { - cb[i/2] = input_image + width*height + width/2*((i+j)/2); - cr[i/2] = input_image + width*height + width*height/4 + width/2*((i+j)/2); - } - } - jpeg_write_raw_data(&cinfo, data, 16); - } - - jpeg_finish_compress(&cinfo); - jpeg_image_size = jpeg_mem_size(&cinfo); - jpeg_destroy_compress(&cinfo); - - return jpeg_image_size; + int i, j, jpeg_image_size; + + JSAMPROW y[16],cb[16],cr[16]; // y[2][5] = color sample of row 2 and pixel column 5; (one plane) + JSAMPARRAY data[3]; // t[0][2][5] = color sample 0 of row 2 and column 5 + + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + + data[0] = y; + data[1] = cb; + data[2] = cr; + + cinfo.err = jpeg_std_error(&jerr); // errors get written to stderr + + jpeg_create_compress(&cinfo); + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = 3; + jpeg_set_defaults (&cinfo); + + jpeg_set_colorspace(&cinfo, JCS_YCbCr); + + cinfo.raw_data_in = TRUE; // supply downsampled data + cinfo.comp_info[0].h_samp_factor = 2; + cinfo.comp_info[0].v_samp_factor = 2; + cinfo.comp_info[1].h_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[2].h_samp_factor = 1; + cinfo.comp_info[2].v_samp_factor = 1; + + jpeg_set_quality(&cinfo, quality, TRUE); + cinfo.dct_method = JDCT_FASTEST; + + jpeg_mem_dest(&cinfo, dest_image, image_size); // data written to mem + + jpeg_start_compress (&cinfo, TRUE); + + for (j = 0; j < height; j += 16) { + for (i = 0; i < 16; i++) { + y[i] = input_image + width * (i + j); + + if (i % 2 == 0) { + cb[i / 2] = input_image + width * height + width / 2 * ((i + j) /2); + cr[i / 2] = input_image + width * height + width * height / 4 + width / 2 * ((i + j) / 2); + } + } + jpeg_write_raw_data(&cinfo, data, 16); + } + + jpeg_finish_compress(&cinfo); + jpeg_image_size = jpeg_mem_size(&cinfo); + jpeg_destroy_compress(&cinfo); + + return jpeg_image_size; } /* put_jpeg_grey_memory converts an input image in the grayscale format into a jpeg image @@ -161,38 +162,38 @@ static int put_jpeg_yuv420p_memory(unsigned char *dest_image, int image_size, */ static int put_jpeg_grey_memory(unsigned char *dest_image, int image_size, unsigned char *input_image, int width, int height, int quality) { - int y, dest_image_size; - JSAMPROW row_ptr[1]; - struct jpeg_compress_struct cjpeg; - struct jpeg_error_mgr jerr; - - cjpeg.err = jpeg_std_error(&jerr); - jpeg_create_compress(&cjpeg); - cjpeg.image_width = width; - cjpeg.image_height = height; - cjpeg.input_components = 1; /* one colour component */ - cjpeg.in_color_space = JCS_GRAYSCALE; - - jpeg_set_defaults(&cjpeg); - - jpeg_set_quality(&cjpeg, quality, TRUE); - cjpeg.dct_method = JDCT_FASTEST; - jpeg_mem_dest(&cjpeg, dest_image, image_size); // data written to mem - - jpeg_start_compress (&cjpeg, TRUE); - - row_ptr[0] = input_image; - - for (y = 0; y < height; y++) { - jpeg_write_scanlines(&cjpeg, row_ptr, 1); - row_ptr[0] += width; - } - - jpeg_finish_compress(&cjpeg); - dest_image_size = jpeg_mem_size(&cjpeg); - jpeg_destroy_compress(&cjpeg); - - return dest_image_size; + int y, dest_image_size; + JSAMPROW row_ptr[1]; + struct jpeg_compress_struct cjpeg; + struct jpeg_error_mgr jerr; + + cjpeg.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cjpeg); + cjpeg.image_width = width; + cjpeg.image_height = height; + cjpeg.input_components = 1; /* one colour component */ + cjpeg.in_color_space = JCS_GRAYSCALE; + + jpeg_set_defaults(&cjpeg); + + jpeg_set_quality(&cjpeg, quality, TRUE); + cjpeg.dct_method = JDCT_FASTEST; + jpeg_mem_dest(&cjpeg, dest_image, image_size); // data written to mem + + jpeg_start_compress (&cjpeg, TRUE); + + row_ptr[0] = input_image; + + for (y = 0; y < height; y++) { + jpeg_write_scanlines(&cjpeg, row_ptr, 1); + row_ptr[0] += width; + } + + jpeg_finish_compress(&cjpeg); + dest_image_size = jpeg_mem_size(&cjpeg); + jpeg_destroy_compress(&cjpeg); + + return dest_image_size; } /* put_jpeg_yuv420p_file converts an YUV420P coded image to a jpeg image and writes @@ -207,55 +208,55 @@ static int put_jpeg_grey_memory(unsigned char *dest_image, int image_size, unsig */ static void put_jpeg_yuv420p_file(FILE *fp, unsigned char *image, int width, int height, int quality) { - int i,j; - - JSAMPROW y[16],cb[16],cr[16]; // y[2][5] = color sample of row 2 and pixel column 5; (one plane) - JSAMPARRAY data[3]; // t[0][2][5] = color sample 0 of row 2 and column 5 - - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; - - data[0] = y; - data[1] = cb; - data[2] = cr; - - cinfo.err = jpeg_std_error(&jerr); // errors get written to stderr - - jpeg_create_compress(&cinfo); - cinfo.image_width = width; - cinfo.image_height = height; - cinfo.input_components = 3; - jpeg_set_defaults(&cinfo); - - jpeg_set_colorspace(&cinfo, JCS_YCbCr); - - cinfo.raw_data_in = TRUE; // supply downsampled data - cinfo.comp_info[0].h_samp_factor = 2; - cinfo.comp_info[0].v_samp_factor = 2; - cinfo.comp_info[1].h_samp_factor = 1; - cinfo.comp_info[1].v_samp_factor = 1; - cinfo.comp_info[2].h_samp_factor = 1; - cinfo.comp_info[2].v_samp_factor = 1; - - jpeg_set_quality(&cinfo, quality, TRUE); - cinfo.dct_method = JDCT_FASTEST; - - jpeg_stdio_dest(&cinfo, fp); // data written to file - jpeg_start_compress(&cinfo, TRUE); - - for (j = 0; j < height; j += 16) { - for (i = 0; i < 16; i++) { - y[i] = image + width*(i+j); - if (i%2 == 0) { - cb[i/2] = image + width*height + width/2*((i+j)/2); - cr[i/2] = image + width*height + width*height/4 + width/2*((i+j)/2); - } - } - jpeg_write_raw_data(&cinfo, data, 16); - } - - jpeg_finish_compress(&cinfo); - jpeg_destroy_compress(&cinfo); + int i, j; + + JSAMPROW y[16],cb[16],cr[16]; // y[2][5] = color sample of row 2 and pixel column 5; (one plane) + JSAMPARRAY data[3]; // t[0][2][5] = color sample 0 of row 2 and column 5 + + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + + data[0] = y; + data[1] = cb; + data[2] = cr; + + cinfo.err = jpeg_std_error(&jerr); // errors get written to stderr + + jpeg_create_compress(&cinfo); + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = 3; + jpeg_set_defaults(&cinfo); + + jpeg_set_colorspace(&cinfo, JCS_YCbCr); + + cinfo.raw_data_in = TRUE; // supply downsampled data + cinfo.comp_info[0].h_samp_factor = 2; + cinfo.comp_info[0].v_samp_factor = 2; + cinfo.comp_info[1].h_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[2].h_samp_factor = 1; + cinfo.comp_info[2].v_samp_factor = 1; + + jpeg_set_quality(&cinfo, quality, TRUE); + cinfo.dct_method = JDCT_FASTEST; + + jpeg_stdio_dest(&cinfo, fp); // data written to file + jpeg_start_compress(&cinfo, TRUE); + + for (j = 0; j < height; j += 16) { + for (i = 0; i < 16; i++) { + y[i] = image + width * (i + j); + if (i % 2 == 0) { + cb[i / 2] = image + width * height + width / 2 * ((i + j) / 2); + cr[i / 2] = image + width * height + width * height / 4 + width / 2 * ((i + j) / 2); + } + } + jpeg_write_raw_data(&cinfo, data, 16); + } + + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); } @@ -271,33 +272,33 @@ static void put_jpeg_yuv420p_file(FILE *fp, unsigned char *image, int width, int */ static void put_jpeg_grey_file(FILE *picture, unsigned char *image, int width, int height, int quality) { - int y; - JSAMPROW row_ptr[1]; - struct jpeg_compress_struct cjpeg; - struct jpeg_error_mgr jerr; - - cjpeg.err = jpeg_std_error(&jerr); - jpeg_create_compress(&cjpeg); - cjpeg.image_width = width; - cjpeg.image_height = height; - cjpeg.input_components = 1; /* one colour component */ - cjpeg.in_color_space = JCS_GRAYSCALE; - - jpeg_set_defaults(&cjpeg); - - jpeg_set_quality(&cjpeg, quality, TRUE); - cjpeg.dct_method = JDCT_FASTEST; - jpeg_stdio_dest(&cjpeg, picture); - - jpeg_start_compress(&cjpeg, TRUE); - - row_ptr[0] = image; - for (y = 0; y < height; y++) { - jpeg_write_scanlines(&cjpeg, row_ptr, 1); - row_ptr[0] += width; - } - jpeg_finish_compress(&cjpeg); - jpeg_destroy_compress(&cjpeg); + int y; + JSAMPROW row_ptr[1]; + struct jpeg_compress_struct cjpeg; + struct jpeg_error_mgr jerr; + + cjpeg.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cjpeg); + cjpeg.image_width = width; + cjpeg.image_height = height; + cjpeg.input_components = 1; /* one colour component */ + cjpeg.in_color_space = JCS_GRAYSCALE; + + jpeg_set_defaults(&cjpeg); + + jpeg_set_quality(&cjpeg, quality, TRUE); + cjpeg.dct_method = JDCT_FASTEST; + jpeg_stdio_dest(&cjpeg, picture); + + jpeg_start_compress(&cjpeg, TRUE); + + row_ptr[0] = image; + for (y = 0; y < height; y++) { + jpeg_write_scanlines(&cjpeg, row_ptr, 1); + row_ptr[0] += width; + } + jpeg_finish_compress(&cjpeg); + jpeg_destroy_compress(&cjpeg); } @@ -312,158 +313,158 @@ static void put_jpeg_grey_file(FILE *picture, unsigned char *image, int width, i */ static void put_ppm_bgr24_file(FILE *picture, unsigned char *image, int width, int height) { - int x, y; - unsigned char *l = image; - unsigned char *u = image+width*height; - unsigned char *v = u+(width*height)/4; - int r, g, b; - int warningkiller; - unsigned char rgb[3]; - - /* ppm header - * width height - * maxval - */ - fprintf(picture, "P6\n"); - fprintf(picture, "%d %d\n", width, height); - fprintf(picture, "%d\n", 255); - for (y = 0; y < height; y++) { - - for (x = 0; x < width; x++) { - r = 76283*(((int)*l)-16)+104595*(((int)*u)-128); - g = 76283*(((int)*l)-16)- 53281*(((int)*u)-128)-25625*(((int)*v)-128); - b = 76283*(((int)*l)-16)+132252*(((int)*v)-128); - r = r>>16; - g = g>>16; - b = b>>16; - if (r < 0) - r = 0; - else if (r > 255) - r = 255; - if (g < 0) - g = 0; - else if (g > 255) - g = 255; - if (b < 0) - b = 0; - else if (b > 255) - b = 255; - - rgb[0] = b; - rgb[1] = g; - rgb[2] = r; - - l++; - if (x & 1) { - u++; - v++; - } - /* ppm is rgb not bgr */ - warningkiller = fwrite(rgb, 1, 3, picture); - } - if (y & 1) { - u -= width/2; - v -= width/2; - } - } + int x, y; + unsigned char *l = image; + unsigned char *u = image + width * height; + unsigned char *v = u + (width * height) / 4; + int r, g, b; + int warningkiller; + unsigned char rgb[3]; + + /* ppm header + * width height + * maxval + */ + fprintf(picture, "P6\n"); + fprintf(picture, "%d %d\n", width, height); + fprintf(picture, "%d\n", 255); + for (y = 0; y < height; y++) { + + for (x = 0; x < width; x++) { + r = 76283 * (((int)*l) - 16)+104595*(((int)*u) - 128); + g = 76283 * (((int)*l) - 16)- 53281*(((int)*u) - 128) - 25625 * (((int)*v) - 128); + b = 76283 * (((int)*l) - 16) + 132252 * (((int)*v) - 128); + r = r >> 16; + g = g >> 16; + b = b >> 16; + if (r < 0) + r = 0; + else if (r > 255) + r = 255; + if (g < 0) + g = 0; + else if (g > 255) + g = 255; + if (b < 0) + b = 0; + else if (b > 255) + b = 255; + + rgb[0] = b; + rgb[1] = g; + rgb[2] = r; + + l++; + if (x & 1) { + u++; + v++; + } + /* ppm is rgb not bgr */ + warningkiller = fwrite(rgb, 1, 3, picture); + } + if (y & 1) { + u -= width / 2; + v -= width / 2; + } + } } /* copy smartmask as an overlay into motion images and movies */ void overlay_smartmask(struct context *cnt, unsigned char *out) { - int i, x, v, width, height, line; - struct images *imgs = &cnt->imgs; - unsigned char *smartmask = imgs->smartmask_final; - unsigned char *out_y, *out_u, *out_v; - - i = imgs->motionsize; - v = i + ((imgs->motionsize) / 4); - width = imgs->width; - height = imgs->height; - - /* set V to 255 to make smartmask appear red */ - out_v = out + v; - out_u = out + i; - for (i = 0; i < height; i += 2){ - line = i * width; - for (x = 0; x < width; x += 2){ - if (smartmask[line + x] == 0 || - smartmask[line + x + 1] == 0 || - smartmask[line + width + x] == 0 || - smartmask[line + width + x + 1] == 0){ - *out_v = 255; - *out_u = 128; - } - out_v++; - out_u++; - } - } - out_y = out; - /* set colour intensity for smartmask */ - for (i = 0; i < imgs->motionsize; i++){ - if (smartmask[i] == 0) - *out_y = 0; - out_y++; - } + int i, x, v, width, height, line; + struct images *imgs = &cnt->imgs; + unsigned char *smartmask = imgs->smartmask_final; + unsigned char *out_y, *out_u, *out_v; + + i = imgs->motionsize; + v = i + ((imgs->motionsize) / 4); + width = imgs->width; + height = imgs->height; + + /* set V to 255 to make smartmask appear red */ + out_v = out + v; + out_u = out + i; + for (i = 0; i < height; i += 2) { + line = i * width; + for (x = 0; x < width; x += 2) { + if (smartmask[line + x] == 0 || smartmask[line + x + 1] == 0 || + smartmask[line + width + x] == 0 || + smartmask[line + width + x + 1] == 0) { + + *out_v = 255; + *out_u = 128; + } + out_v++; + out_u++; + } + } + out_y = out; + /* set colour intensity for smartmask */ + for (i = 0; i < imgs->motionsize; i++) { + if (smartmask[i] == 0) + *out_y = 0; + out_y++; + } } /* copy fixed mask as an overlay into motion images and movies */ void overlay_fixed_mask(struct context *cnt, unsigned char *out) { - int i; - struct images *imgs = &cnt->imgs; - unsigned char *motion_img = imgs->out; - unsigned char *mask = imgs->mask; - int pixel; - - /* set y to mask + motion-pixel to keep motion pixels visible on grey background*/ - for (i = 0; i < imgs->motionsize; i++){ - pixel = 255-mask[i]+motion_img[i]; - if (pixel > 255) - *out = 255; - else - *out = pixel; - out++; - } + int i; + struct images *imgs = &cnt->imgs; + unsigned char *motion_img = imgs->out; + unsigned char *mask = imgs->mask; + int pixel; + + /* set y to mask + motion-pixel to keep motion pixels visible on grey background*/ + for (i = 0; i < imgs->motionsize; i++) { + pixel = 255-mask[i]+motion_img[i]; + if (pixel > 255) + *out = 255; + else + *out = pixel; + out++; + } } /* copy largest label as an overlay into motion images and movies */ void overlay_largest_label(struct context *cnt, unsigned char *out) { - int i, x, v, width, height, line; - struct images *imgs = &cnt->imgs; - int *labels = imgs->labels; - unsigned char *out_y, *out_u, *out_v; - - i = imgs->motionsize; - v = i+((imgs->motionsize)/4); - width = imgs->width; - height = imgs->height; - - /* set U to 255 to make label appear blue */ - out_u = out+i; - out_v = out+v; - for (i = 0; i < height; i += 2){ - line = i*width; - for (x = 0; x < width; x += 2){ - if (labels[line+x] & 32768 || - labels[line+x+1] & 32768 || - labels[line+width+x] & 32768 || - labels[line+width+x+1] & 32768) { - *out_u = 255; - *out_v = 128; - } - out_u++; - out_v++; - } - } - out_y = out; - /* set intensity for coloured label to have better visibility */ - for (i = 0; i < imgs->motionsize; i++) { - if (*labels++ & 32768) - *out_y = 0; - out_y++; - } + int i, x, v, width, height, line; + struct images *imgs = &cnt->imgs; + int *labels = imgs->labels; + unsigned char *out_y, *out_u, *out_v; + + i = imgs->motionsize; + v = i + ((imgs->motionsize) / 4); + width = imgs->width; + height = imgs->height; + + /* set U to 255 to make label appear blue */ + out_u = out + i; + out_v = out + v; + for (i = 0; i < height; i += 2) { + line = i * width; + for (x = 0; x < width; x += 2) { + if (labels[line + x] & 32768 || labels[line + x + 1] & 32768 || + labels[line + width + x] & 32768 || + labels[line + width + x + 1] & 32768) { + + *out_u = 255; + *out_v = 128; + } + out_u++; + out_v++; + } + } + out_y = out; + /* set intensity for coloured label to have better visibility */ + for (i = 0; i < imgs->motionsize; i++) { + if (*labels++ & 32768) + *out_y = 0; + out_y++; + } } /* put_picture_mem is used for the webcam feature. Depending on the image type @@ -481,125 +482,125 @@ void overlay_largest_label(struct context *cnt, unsigned char *out) int put_picture_memory(struct context *cnt, unsigned char* dest_image, int image_size, unsigned char *image, int quality) { - switch (cnt->imgs.type) { - case VIDEO_PALETTE_YUV420P: - return put_jpeg_yuv420p_memory(dest_image, image_size, image, - cnt->imgs.width, cnt->imgs.height, quality); - case VIDEO_PALETTE_GREY: - return put_jpeg_grey_memory(dest_image, image_size, image, - cnt->imgs.width, cnt->imgs.height, quality); - } - - return 0; + switch (cnt->imgs.type) { + case VIDEO_PALETTE_YUV420P: + return put_jpeg_yuv420p_memory(dest_image, image_size, image, + cnt->imgs.width, cnt->imgs.height, quality); + case VIDEO_PALETTE_GREY: + return put_jpeg_grey_memory(dest_image, image_size, image, + cnt->imgs.width, cnt->imgs.height, quality); + } + + return 0; } void put_picture_fd(struct context *cnt, FILE *picture, unsigned char *image, int quality) { - if (cnt->imgs.picture_type == IMAGE_TYPE_PPM) { - put_ppm_bgr24_file(picture, image, cnt->imgs.width, cnt->imgs.height); - } else { - switch (cnt->imgs.type) { - case VIDEO_PALETTE_YUV420P: - put_jpeg_yuv420p_file(picture, image, cnt->imgs.width, cnt->imgs.height, quality); - break; - case VIDEO_PALETTE_GREY: - put_jpeg_grey_file(picture, image, cnt->imgs.width, cnt->imgs.height, quality); - break; - } - } + if (cnt->imgs.picture_type == IMAGE_TYPE_PPM) { + put_ppm_bgr24_file(picture, image, cnt->imgs.width, cnt->imgs.height); + } else { + switch (cnt->imgs.type) { + case VIDEO_PALETTE_YUV420P: + put_jpeg_yuv420p_file(picture, image, cnt->imgs.width, cnt->imgs.height, quality); + break; + case VIDEO_PALETTE_GREY: + put_jpeg_grey_file(picture, image, cnt->imgs.width, cnt->imgs.height, quality); + break; + } + } } void put_picture(struct context *cnt, char *file, unsigned char *image, int ftype) { - FILE *picture; - - picture = myfopen(file, "w"); - if (!picture) { - /* Report to syslog - suggest solution if the problem is access rights to target dir */ - if (errno == EACCES) { - motion_log(LOG_ERR, 1, - "%s: Can't write picture to file %s - check access rights to target directory" - "Thread is going to finish due to this fatal error", - __FUNCTION__, file); - cnt->finish = 1; - cnt->restart = 0; - return; - } else { - /* If target dir is temporarily unavailable we may survive */ - motion_log(LOG_ERR, 1, "%s: Can't write picture to file %s", __FUNCTION__, file); - return; - } - } - - put_picture_fd(cnt, picture, image, cnt->conf.quality); - fclose(picture); - event(cnt, EVENT_FILECREATE, NULL, file, (void *)(unsigned long)ftype, NULL); + FILE *picture; + + picture = myfopen(file, "w"); + if (!picture) { + /* Report to syslog - suggest solution if the problem is access rights to target dir */ + if (errno == EACCES) { + motion_log(LOG_ERR, 1, + "%s: Can't write picture to file %s - check access rights to target directory" + "Thread is going to finish due to this fatal error", + __FUNCTION__, file); + cnt->finish = 1; + cnt->restart = 0; + return; + } else { + /* If target dir is temporarily unavailable we may survive */ + motion_log(LOG_ERR, 1, "%s: Can't write picture to file %s", __FUNCTION__, file); + return; + } + } + + put_picture_fd(cnt, picture, image, cnt->conf.quality); + fclose(picture); + event(cnt, EVENT_FILECREATE, NULL, file, (void *)(unsigned long)ftype, NULL); } /* Get the pgm file used as fixed mask */ unsigned char *get_pgm(FILE *picture, int width, int height) { - int x = 0 , y = 0, maxval; - char line[256]; - unsigned char *image; - - line[255] = 0; - - if (!fgets(line, 255, picture)) { - motion_log(LOG_ERR, 1, "%s: Could not read from ppm file", __FUNCTION__); - return NULL; - } - - if (strncmp(line, "P5", 2)) { - motion_log(LOG_ERR, 1, "%s: This is not a ppm file, starts with '%s'", - __FUNCTION__, line); - return NULL; - } - - /* skip comment */ - line[0] = '#'; - while (line[0] == '#') - if (!fgets(line, 255, picture)) - return NULL; - - /* check size */ - if (sscanf(line, "%d %d", &x, &y) != 2) { - motion_log(LOG_ERR, 1, "%s: Failed reading size in pgm file", __FUNCTION__); - return NULL; - } - - if (x != width || y != height) { - motion_log(LOG_ERR, 1, "%s: Wrong image size %dx%d should be %dx%d", - __FUNCTION__, x, y, width, height); - return NULL; - } - - /* Maximum value */ - line[0] = '#'; - while (line[0] == '#') - if (!fgets(line, 255, picture)) - return NULL; - - if (sscanf(line, "%d", &maxval) != 1) { - motion_log(LOG_ERR, 1, "%s: Failed reading maximum value in pgm file", __FUNCTION__); - return NULL; - } - - /* read data */ - - image = mymalloc(width * height); - - for (y = 0; y < height; y++) { - if ((int)fread(&image[y * width], 1, width, picture) != width) - motion_log(LOG_ERR, 1, "%s: Failed reading image data from pgm file", __FUNCTION__); - - for (x = 0; x < width; x++) { - image[y * width + x] = (int)image[y * width + x] * 255 / maxval; - } - } - - return image; + int x = 0 , y = 0, maxval; + char line[256]; + unsigned char *image; + + line[255] = 0; + + if (!fgets(line, 255, picture)) { + motion_log(LOG_ERR, 1, "%s: Could not read from ppm file", __FUNCTION__); + return NULL; + } + + if (strncmp(line, "P5", 2)) { + motion_log(LOG_ERR, 1, "%s: This is not a ppm file, starts with '%s'", + __FUNCTION__, line); + return NULL; + } + + /* skip comment */ + line[0] = '#'; + while (line[0] == '#') + if (!fgets(line, 255, picture)) + return NULL; + + /* check size */ + if (sscanf(line, "%d %d", &x, &y) != 2) { + motion_log(LOG_ERR, 1, "%s: Failed reading size in pgm file", __FUNCTION__); + return NULL; + } + + if (x != width || y != height) { + motion_log(LOG_ERR, 1, "%s: Wrong image size %dx%d should be %dx%d", + __FUNCTION__, x, y, width, height); + return NULL; + } + + /* Maximum value */ + line[0] = '#'; + while (line[0] == '#') + if (!fgets(line, 255, picture)) + return NULL; + + if (sscanf(line, "%d", &maxval) != 1) { + motion_log(LOG_ERR, 1, "%s: Failed reading maximum value in pgm file", __FUNCTION__); + return NULL; + } + + /* read data */ + + image = mymalloc(width * height); + + for (y = 0; y < height; y++) { + if ((int)fread(&image[y * width], 1, width, picture) != width) + motion_log(LOG_ERR, 1, "%s: Failed reading image data from pgm file", __FUNCTION__); + + for (x = 0; x < width; x++) { + image[y * width + x] = (int)image[y * width + x] * 255 / maxval; + } + } + + return image; } /* If a mask file is asked for but does not exist this function @@ -608,90 +609,90 @@ unsigned char *get_pgm(FILE *picture, int width, int height) */ void put_fixed_mask(struct context *cnt, const char *file) { - FILE *picture; - - picture = myfopen(file, "w"); - if (!picture) { - /* Report to syslog - suggest solution if the problem is access rights to target dir */ - if (errno == EACCES) { - motion_log(LOG_ERR, 1, - "%s: can't write mask file %s - check access rights to target directory", - __FUNCTION__, file); - } else { - /* If target dir is temporarily unavailable we may survive */ - motion_log(LOG_ERR, 1, "%s: can't write mask file %s", __FUNCTION__, file); - } - return; - } - memset(cnt->imgs.out, 255, cnt->imgs.motionsize); /* initialize to unset */ - - /* Write pgm-header */ - fprintf(picture, "P5\n"); - fprintf(picture, "%d %d\n", cnt->conf.width, cnt->conf.height); - fprintf(picture, "%d\n", 255); - - /* write pgm image data at once */ - if ((int)fwrite(cnt->imgs.out, cnt->conf.width, cnt->conf.height, picture) != cnt->conf.height) { - motion_log(LOG_ERR, 1, "%s: Failed writing default mask as pgm file", __FUNCTION__); - return; - } - - fclose(picture); - - motion_log(LOG_ERR, 0, "%s: Creating empty mask %s\nPlease edit this file and " - "re-run motion to enable mask feature", __FUNCTION__, cnt->conf.mask_file); + FILE *picture; + + picture = myfopen(file, "w"); + if (!picture) { + /* Report to syslog - suggest solution if the problem is access rights to target dir */ + if (errno == EACCES) { + motion_log(LOG_ERR, 1, + "%s: can't write mask file %s - check access rights to target directory", + __FUNCTION__, file); + } else { + /* If target dir is temporarily unavailable we may survive */ + motion_log(LOG_ERR, 1, "%s: can't write mask file %s", __FUNCTION__, file); + } + return; + } + memset(cnt->imgs.out, 255, cnt->imgs.motionsize); /* initialize to unset */ + + /* Write pgm-header */ + fprintf(picture, "P5\n"); + fprintf(picture, "%d %d\n", cnt->conf.width, cnt->conf.height); + fprintf(picture, "%d\n", 255); + + /* write pgm image data at once */ + if ((int)fwrite(cnt->imgs.out, cnt->conf.width, cnt->conf.height, picture) != cnt->conf.height) { + motion_log(LOG_ERR, 1, "%s: Failed writing default mask as pgm file", __FUNCTION__); + return; + } + + fclose(picture); + + motion_log(LOG_ERR, 0, "%s: Creating empty mask %s\nPlease edit this file and " + "re-run motion to enable mask feature", __FUNCTION__, cnt->conf.mask_file); } /* save preview_shot */ void preview_save(struct context *cnt) { #ifdef HAVE_FFMPEG - int use_imagepath; - int basename_len; + int use_imagepath; + int basename_len; #endif /* HAVE_FFMPEG */ - const char *imagepath; - char previewname[PATH_MAX]; - char filename[PATH_MAX]; - struct image_data *saved_current_image; + const char *imagepath; + char previewname[PATH_MAX]; + char filename[PATH_MAX]; + struct image_data *saved_current_image; - if (cnt->imgs.preview_image.diffs) { - /* Save current global context */ - saved_current_image = cnt->current_image; - /* Set global context to the image we are processing */ - cnt->current_image = &cnt->imgs.preview_image; + if (cnt->imgs.preview_image.diffs) { + /* Save current global context */ + saved_current_image = cnt->current_image; + /* Set global context to the image we are processing */ + cnt->current_image = &cnt->imgs.preview_image; #ifdef HAVE_FFMPEG - /* Use filename of movie i.o. jpeg_filename when set to 'preview' */ - use_imagepath = strcmp(cnt->conf.imagepath, "preview"); - - if (cnt->ffmpeg_output && !use_imagepath){ - /* Replace avi/mpg with jpg/ppm and keep the rest of the filename */ - basename_len = strlen(cnt->newfilename) - 3; - strncpy(previewname, cnt->newfilename, basename_len); - previewname[basename_len] = '\0'; - strcat(previewname, imageext(cnt)); - put_picture(cnt, previewname, cnt->imgs.preview_image.image , FTYPE_IMAGE); - } else + /* Use filename of movie i.o. jpeg_filename when set to 'preview' */ + use_imagepath = strcmp(cnt->conf.imagepath, "preview"); + + if (cnt->ffmpeg_output && !use_imagepath) { + /* Replace avi/mpg with jpg/ppm and keep the rest of the filename */ + basename_len = strlen(cnt->newfilename) - 3; + strncpy(previewname, cnt->newfilename, basename_len); + previewname[basename_len] = '\0'; + strcat(previewname, imageext(cnt)); + put_picture(cnt, previewname, cnt->imgs.preview_image.image , FTYPE_IMAGE); + } else #endif /* HAVE_FFMPEG */ - { - /* Save best preview-shot also when no movies are recorded or imagepath - * is used. Filename has to be generated - nothing available to reuse! */ - //printf("preview_shot: different filename or picture only!\n"); - - /* conf.imagepath would normally be defined but if someone deleted it by control interface - * it is better to revert to the default than fail */ - if (cnt->conf.imagepath) - imagepath = cnt->conf.imagepath; - else - imagepath = (char *)DEF_IMAGEPATH; - - mystrftime(cnt, filename, sizeof(filename), imagepath, &cnt->imgs.preview_image.timestamp_tm, NULL, 0); - snprintf(previewname, PATH_MAX, "%s/%s.%s", cnt->conf.filepath, filename, imageext(cnt)); - - put_picture(cnt, previewname, cnt->imgs.preview_image.image, FTYPE_IMAGE); - } - - /* restore global context values */ - cnt->current_image = saved_current_image; - } + { + /* Save best preview-shot also when no movies are recorded or imagepath + * is used. Filename has to be generated - nothing available to reuse! */ + //printf("preview_shot: different filename or picture only!\n"); + + /* conf.imagepath would normally be defined but if someone deleted it by control interface + * it is better to revert to the default than fail */ + if (cnt->conf.imagepath) + imagepath = cnt->conf.imagepath; + else + imagepath = (char *)DEF_IMAGEPATH; + + mystrftime(cnt, filename, sizeof(filename), imagepath, &cnt->imgs.preview_image.timestamp_tm, NULL, 0); + snprintf(previewname, PATH_MAX, "%s/%s.%s", cnt->conf.filepath, filename, imageext(cnt)); + + put_picture(cnt, previewname, cnt->imgs.preview_image.image, FTYPE_IMAGE); + } + + /* restore global context values */ + cnt->current_image = saved_current_image; + } } diff --git a/rotate.c b/rotate.c index fb7755b..39ab0ea 100644 --- a/rotate.c +++ b/rotate.c @@ -1,33 +1,33 @@ /* - * rotate.c + * rotate.c * - * Module for handling image rotation. + * Module for handling image rotation. * - * Copyright 2004-2005, Per Jonsson (per@pjd.nu) - * - * This software is distributed under the GNU Public license - * Version 2. See also the file 'COPYING'. + * Copyright 2004-2005, Per Jonsson (per@pjd.nu) + * + * This software is distributed under the GNU Public license + * Version 2. See also the file 'COPYING'. * - * Image rotation is a feature of Motion that can be used when the - * camera is mounted upside-down or on the side. The module only - * supports rotation in multiples of 90 degrees. Using rotation - * increases the Motion CPU usage slightly. + * Image rotation is a feature of Motion that can be used when the + * camera is mounted upside-down or on the side. The module only + * supports rotation in multiples of 90 degrees. Using rotation + * increases the Motion CPU usage slightly. * - * Version history: - * v6 (29-Aug-2005) - simplified the code as Motion now requires - * that width and height are multiples of 16 - * v5 (3-Aug-2005) - cleanup in code comments - * - better adherence to coding standard - * - fix for __bswap_32 macro collision - * - fixed bug where initialization would be - * incomplete for invalid degrees of rotation - * - now uses motion_log for error reporting - * v4 (26-Oct-2004) - new fix for width/height from imgs/conf due to - * earlier misinterpretation - * v3 (11-Oct-2004) - cleanup of width/height from imgs/conf - * v2 (26-Sep-2004) - separation of capture/internal dimensions - * - speed optimization, including bswap - * v1 (28-Aug-2004) - initial version + * Version history: + * v6 (29-Aug-2005) - simplified the code as Motion now requires + * that width and height are multiples of 16 + * v5 (3-Aug-2005) - cleanup in code comments + * - better adherence to coding standard + * - fix for __bswap_32 macro collision + * - fixed bug where initialization would be + * incomplete for invalid degrees of rotation + * - now uses motion_log for error reporting + * v4 (26-Oct-2004) - new fix for width/height from imgs/conf due to + * earlier misinterpretation + * v3 (11-Oct-2004) - cleanup of width/height from imgs/conf + * v2 (26-Sep-2004) - separation of capture/internal dimensions + * - speed optimization, including bswap + * v1 (28-Aug-2004) - initial version */ #include "rotate.h" @@ -36,11 +36,11 @@ * We don't have a 32-bit unsigned integer type, so define it, given * a 32-bit type was found by configure. */ -# ifdef TYPE_32BIT +# ifdef TYPE_32BIT typedef unsigned TYPE_32BIT __uint32; -# else -# error "Failed to find a 32-bit integer type." -# endif +# else +# error "Failed to find a 32-bit integer type." +# endif #endif /*============================================================================= @@ -65,52 +65,52 @@ typedef unsigned TYPE_32BIT __uint32; */ /* Swap bytes in 32 bit value. This is used as a fallback and for constants. */ -#define rot__bswap_constant_32(x) \ - ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ - (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) +#define rot__bswap_constant_32(x) \ + ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ + (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) #ifdef __GNUC__ -# if (__GNUC__ >= 2) && (i386 || __i386 || __i386__) +# if (__GNUC__ >= 2) && (i386 || __i386 || __i386__) /* We're on an Intel-compatible platform, so we can use inline Intel assembler * for the swapping. */ -# ifndef HAVE_BSWAP +# ifndef HAVE_BSWAP /* Bswap is not available, we have to use three instructions instead. */ -# define rot__bswap_32(x) \ - (__extension__ \ - ({ register __uint32 __v, __x = (x); \ - if (__builtin_constant_p (__x)) \ - __v = rot__bswap_constant_32 (__x); \ - else \ - __asm__ ("rorw $8, %w0;" \ - "rorl $16, %0;" \ - "rorw $8, %w0" \ - : "=r" (__v) \ - : "0" (__x) \ - : "cc"); \ - __v; })) -# else -# define rot__bswap_32(x) \ - (__extension__ \ - ({ register __uint32 __v, __x = (x); \ - if (__builtin_constant_p (__x)) \ - __v = rot__bswap_constant_32 (__x); \ - else \ - __asm__ ("bswap %0" : "=r" (__v) : "0" (__x)); \ - __v; })) -# endif -# else +# define rot__bswap_32(x) \ + (__extension__ \ + ({ register __uint32 __v, __x = (x); \ + if (__builtin_constant_p (__x)) \ + __v = rot__bswap_constant_32 (__x); \ + else \ + __asm__ ("rorw $8, %w0;" \ + "rorl $16, %0;" \ + "rorw $8, %w0" \ + : "=r" (__v) \ + : "0" (__x) \ + : "cc"); \ + __v; })) +# else +# define rot__bswap_32(x) \ + (__extension__ \ + ({ register __uint32 __v, __x = (x); \ + if (__builtin_constant_p (__x)) \ + __v = rot__bswap_constant_32 (__x); \ + else \ + __asm__ ("bswap %0" : "=r" (__v) : "0" (__x)); \ + __v; })) +# endif +# else /* Non-Intel platform or too old version of gcc. */ -# define rot__bswap_32(x) \ - (__extension__ \ - ({ register __uint32 __x = (x); \ - rot__bswap_constant_32 (__x); })) -# endif +# define rot__bswap_32(x) \ + (__extension__ \ + ({ register __uint32 __x = (x); \ + rot__bswap_constant_32 (__x); })) +# endif #else /* Not a GNU compiler. */ static inline __uint32 rot__bswap_32(__uint32 __bsx) { - return __bswap_constant_32 (__bsx); + return __bswap_constant_32 (__bsx); } #endif @@ -136,15 +136,15 @@ static inline __uint32 rot__bswap_32(__uint32 __bsx) */ static void reverse_inplace_quad(unsigned char *src, int size) { - __uint32 *nsrc = (__uint32 *)src; /* first quad */ - __uint32 *ndst = (__uint32 *)(src + size - 4); /* last quad */ - register __uint32 tmp; + __uint32 *nsrc = (__uint32 *)src; /* first quad */ + __uint32 *ndst = (__uint32 *)(src + size - 4); /* last quad */ + register __uint32 tmp; - while (nsrc < ndst) { - tmp = swap_bytes(*ndst); - *ndst-- = swap_bytes(*nsrc); - *nsrc++ = tmp; - } + while (nsrc < ndst) { + tmp = swap_bytes(*ndst); + *ndst-- = swap_bytes(*nsrc); + *nsrc++ = tmp; + } } /** @@ -167,17 +167,17 @@ static void reverse_inplace_quad(unsigned char *src, int size) static void rot90cw(unsigned char *src, register unsigned char *dst, int size, int width, int height) { - unsigned char *endp; - register unsigned char *base; - int j; + unsigned char *endp; + register unsigned char *base; + int j; - endp = src + size; - for (base = endp - width; base < endp; base++) { - src = base; - for (j = 0; j < height; j++, src -= width) { - *dst++ = *src; - } - } + endp = src + size; + for (base = endp - width; base < endp; base++) { + src = base; + for (j = 0; j < height; j++, src -= width) { + *dst++ = *src; + } + } } /** @@ -200,18 +200,18 @@ static void rot90cw(unsigned char *src, register unsigned char *dst, int size, static inline void rot90ccw(unsigned char *src, register unsigned char *dst, int size, int width, int height) { - unsigned char *endp; - register unsigned char *base; - int j; + unsigned char *endp; + register unsigned char *base; + int j; - endp = src + size; - dst = dst + size - 1; - for(base = endp - width; base < endp; base++) { - src = base; - for(j = 0; j < height; j++, src -= width) { - *dst-- = *src; - } - } + endp = src + size; + dst = dst + size - 1; + for (base = endp - width; base < endp; base++) { + src = base; + for (j = 0; j < height; j++, src -= width) + *dst-- = *src; + + } } /** @@ -228,75 +228,74 @@ static inline void rot90ccw(unsigned char *src, register unsigned char *dst, */ void rotate_init(struct context *cnt) { - int size; - - /* Make sure temp_buf isn't freed if it hasn't been allocated. */ - cnt->rotate_data.temp_buf = NULL; + int size; + + /* Make sure temp_buf isn't freed if it hasn't been allocated. */ + cnt->rotate_data.temp_buf = NULL; - /* Assign the value in conf.rotate_deg to rotate_data.degrees. This way, - * we have a value that is safe from changes caused by motion-control. - */ - if((cnt->conf.rotate_deg % 90) > 0) { - motion_log(LOG_ERR, 0, "%s: Config option \"rotate\" not a multiple of 90: %d", - __FUNCTION__, cnt->conf.rotate_deg); - cnt->conf.rotate_deg = 0; /* disable rotation */ - cnt->rotate_data.degrees = 0; /* force return below */ - } else { - cnt->rotate_data.degrees = cnt->conf.rotate_deg % 360; /* range: 0..359 */ - } + /* Assign the value in conf.rotate_deg to rotate_data.degrees. This way, + * we have a value that is safe from changes caused by motion-control. + */ + if ((cnt->conf.rotate_deg % 90) > 0) { + motion_log(LOG_ERR, 0, "%s: Config option \"rotate\" not a multiple of 90: %d", + __FUNCTION__, cnt->conf.rotate_deg); + cnt->conf.rotate_deg = 0; /* disable rotation */ + cnt->rotate_data.degrees = 0; /* force return below */ + } else { + cnt->rotate_data.degrees = cnt->conf.rotate_deg % 360; /* range: 0..359 */ + } - /* Upon entrance to this function, imgs.width and imgs.height contain the - * capture dimensions (as set in the configuration file, or read from a - * netcam source). - * - * If rotating 90 or 270 degrees, the capture dimensions and output dimensions - * are not the same. Capture dimensions will be contained in cap_width and - * cap_height in cnt->rotate_data, while output dimensions will be contained - * in imgs.width and imgs.height. - */ + /* Upon entrance to this function, imgs.width and imgs.height contain the + * capture dimensions (as set in the configuration file, or read from a + * netcam source). + * + * If rotating 90 or 270 degrees, the capture dimensions and output dimensions + * are not the same. Capture dimensions will be contained in cap_width and + * cap_height in cnt->rotate_data, while output dimensions will be contained + * in imgs.width and imgs.height. + */ - /* 1. Transfer capture dimensions into cap_width and cap_height. */ - cnt->rotate_data.cap_width = cnt->imgs.width; - cnt->rotate_data.cap_height = cnt->imgs.height; + /* 1. Transfer capture dimensions into cap_width and cap_height. */ + cnt->rotate_data.cap_width = cnt->imgs.width; + cnt->rotate_data.cap_height = cnt->imgs.height; - if((cnt->rotate_data.degrees == 90) || (cnt->rotate_data.degrees == 270)) { - /* 2. "Swap" imgs.width and imgs.height. */ - cnt->imgs.width = cnt->rotate_data.cap_height; - cnt->imgs.height = cnt->rotate_data.cap_width; - } + if ((cnt->rotate_data.degrees == 90) || (cnt->rotate_data.degrees == 270)) { + /* 2. "Swap" imgs.width and imgs.height. */ + cnt->imgs.width = cnt->rotate_data.cap_height; + cnt->imgs.height = cnt->rotate_data.cap_width; + } - /* If we're not rotating, let's exit once we have setup the capture dimensions - * and output dimensions properly. - */ - if(cnt->rotate_data.degrees == 0) - return; + /* If we're not rotating, let's exit once we have setup the capture dimensions + * and output dimensions properly. + */ + if (cnt->rotate_data.degrees == 0) + return; - switch(cnt->imgs.type) { - - case VIDEO_PALETTE_YUV420P: - /* For YUV 4:2:0 planar, the memory block used for 90/270 degrees - * rotation needs to be width x height x 1.5 bytes large. - */ - size = cnt->imgs.width * cnt->imgs.height * 3 / 2; - break; - case VIDEO_PALETTE_GREY: - /* For greyscale, the memory block used for 90/270 degrees rotation - * needs to be width x height bytes large. - */ - size = cnt->imgs.width * cnt->imgs.height; - break; - default: - cnt->rotate_data.degrees = 0; - motion_log(LOG_ERR, 0, "%s: Unsupported palette (%d), rotation is disabled", - __FUNCTION__, cnt->imgs.type); - return; - } + switch (cnt->imgs.type) { + case VIDEO_PALETTE_YUV420P: + /* For YUV 4:2:0 planar, the memory block used for 90/270 degrees + * rotation needs to be width x height x 1.5 bytes large. + */ + size = cnt->imgs.width * cnt->imgs.height * 3 / 2; + break; + case VIDEO_PALETTE_GREY: + /* For greyscale, the memory block used for 90/270 degrees rotation + * needs to be width x height bytes large. + */ + size = cnt->imgs.width * cnt->imgs.height; + break; + default: + cnt->rotate_data.degrees = 0; + motion_log(LOG_ERR, 0, "%s: Unsupported palette (%d), rotation is disabled", + __FUNCTION__, cnt->imgs.type); + return; + } - /* Allocate memory if rotating 90 or 270 degrees, because those rotations - * cannot be performed in-place (they can, but it would be too slow). - */ - if((cnt->rotate_data.degrees == 90) || (cnt->rotate_data.degrees == 270)) - cnt->rotate_data.temp_buf = mymalloc(size); + /* Allocate memory if rotating 90 or 270 degrees, because those rotations + * cannot be performed in-place (they can, but it would be too slow). + */ + if ((cnt->rotate_data.degrees == 90) || (cnt->rotate_data.degrees == 270)) + cnt->rotate_data.temp_buf = mymalloc(size); } /** @@ -312,8 +311,8 @@ void rotate_init(struct context *cnt) */ void rotate_deinit(struct context *cnt) { - if(cnt->rotate_data.temp_buf) - free(cnt->rotate_data.temp_buf); + if (cnt->rotate_data.temp_buf) + free(cnt->rotate_data.temp_buf); } /** @@ -334,87 +333,86 @@ void rotate_deinit(struct context *cnt) */ int rotate_map(struct context *cnt, unsigned char *map) { - /* The image format is either YUV 4:2:0 planar, in which case the pixel - * data is divided in three parts: - * Y - width x height bytes - * U - width x height / 4 bytes - * V - as U - * or, it is in greyscale, in which case the pixel data simply consists - * of width x height bytes. - */ - int wh, wh4 = 0, w2 = 0, h2 = 0; /* width*height, width*height/4 etc. */ - int size, deg; - int width, height; + /* The image format is either YUV 4:2:0 planar, in which case the pixel + * data is divided in three parts: + * Y - width x height bytes + * U - width x height / 4 bytes + * V - as U + * or, it is in greyscale, in which case the pixel data simply consists + * of width x height bytes. + */ + int wh, wh4 = 0, w2 = 0, h2 = 0; /* width*height, width*height/4 etc. */ + int size, deg; + int width, height; - deg = cnt->rotate_data.degrees; - width = cnt->rotate_data.cap_width; - height = cnt->rotate_data.cap_height; + deg = cnt->rotate_data.degrees; + width = cnt->rotate_data.cap_width; + height = cnt->rotate_data.cap_height; - /* Pre-calculate some stuff: - * wh - size of the Y plane, or the entire greyscale image - * size - size of the entire memory block - * wh4 - size of the U plane, and the V plane - * w2 - width of the U plane, and the V plane - * h2 - as w2, but height instead - */ - wh = width * height; - if(cnt->imgs.type == VIDEO_PALETTE_YUV420P) { - size = wh * 3 / 2; - wh4 = wh / 4; - w2 = width / 2; - h2 = height / 2; - } - else { /* VIDEO_PALETTE_GREY */ - size = wh; - } + /* Pre-calculate some stuff: + * wh - size of the Y plane, or the entire greyscale image + * size - size of the entire memory block + * wh4 - size of the U plane, and the V plane + * w2 - width of the U plane, and the V plane + * h2 - as w2, but height instead + */ + wh = width * height; + if (cnt->imgs.type == VIDEO_PALETTE_YUV420P) { + size = wh * 3 / 2; + wh4 = wh / 4; + w2 = width / 2; + h2 = height / 2; + } else { /* VIDEO_PALETTE_GREY */ + size = wh; + } - switch (deg) { - case 90: - /* first do the Y part */ - rot90cw(map, cnt->rotate_data.temp_buf, wh, width, height); - if(cnt->imgs.type == VIDEO_PALETTE_YUV420P) { - /* then do U and V */ - rot90cw(map + wh, cnt->rotate_data.temp_buf + wh, wh4, w2, h2); - rot90cw(map + wh + wh4, cnt->rotate_data.temp_buf + wh + wh4, - wh4, w2, h2); - } - - /* then copy back from the temp buffer to map */ - memcpy(map, cnt->rotate_data.temp_buf, size); + switch (deg) { + case 90: + /* first do the Y part */ + rot90cw(map, cnt->rotate_data.temp_buf, wh, width, height); + if (cnt->imgs.type == VIDEO_PALETTE_YUV420P) { + /* then do U and V */ + rot90cw(map + wh, cnt->rotate_data.temp_buf + wh, wh4, w2, h2); + rot90cw(map + wh + wh4, cnt->rotate_data.temp_buf + wh + wh4, + wh4, w2, h2); + } + + /* then copy back from the temp buffer to map */ + memcpy(map, cnt->rotate_data.temp_buf, size); - break; - - case 180: - /* 180 degrees is easy - just reverse the data within - * Y, U and V. - */ - reverse_inplace_quad(map, wh); - if(cnt->imgs.type == VIDEO_PALETTE_YUV420P) { - reverse_inplace_quad(map + wh, wh4); - reverse_inplace_quad(map + wh + wh4, wh4); - } - break; + break; + + case 180: + /* 180 degrees is easy - just reverse the data within + * Y, U and V. + */ + reverse_inplace_quad(map, wh); + if (cnt->imgs.type == VIDEO_PALETTE_YUV420P) { + reverse_inplace_quad(map + wh, wh4); + reverse_inplace_quad(map + wh + wh4, wh4); + } + break; - case 270: + case 270: - /* first do the Y part */ - rot90ccw(map, cnt->rotate_data.temp_buf, wh, width, height); - if(cnt->imgs.type == VIDEO_PALETTE_YUV420P) { - /* then do U and V */ - rot90ccw(map + wh, cnt->rotate_data.temp_buf + wh, wh4, w2, h2); - rot90ccw(map + wh + wh4, cnt->rotate_data.temp_buf + wh + wh4, - wh4, w2, h2); - } - - /* then copy back from the temp buffer to map */ - memcpy(map, cnt->rotate_data.temp_buf, size); - break; - - default: - /* invalid */ - return -1; - } - - return 0; + /* first do the Y part */ + rot90ccw(map, cnt->rotate_data.temp_buf, wh, width, height); + if (cnt->imgs.type == VIDEO_PALETTE_YUV420P) { + /* then do U and V */ + rot90ccw(map + wh, cnt->rotate_data.temp_buf + wh, wh4, w2, h2); + rot90ccw(map + wh + wh4, cnt->rotate_data.temp_buf + wh + wh4, + wh4, w2, h2); + } + + /* then copy back from the temp buffer to map */ + memcpy(map, cnt->rotate_data.temp_buf, size); + break; + + default: + /* invalid */ + return -1; + } + + return 0; } diff --git a/stream.c b/stream.c index 4d94e5d..64cf98f 100644 --- a/stream.c +++ b/stream.c @@ -1,21 +1,21 @@ /* - * stream.c ( based in webcam.c ) - * Streaming using jpeg images over a multipart/x-mixed-replace stream - * Copyright (C) 2002 Jeroen Vreeken (pe1rxq@amsat.org) + * stream.c (based in webcam.c) + * Streaming using jpeg images over a multipart/x-mixed-replace stream + * Copyright (C) 2002 Jeroen Vreeken (pe1rxq@amsat.org) * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "picture.h" @@ -35,99 +35,99 @@ */ int http_bindsock(int port, int local) { - int sl = -1, optval; - struct addrinfo hints, *res = NULL, *ressave = NULL; - char portnumber[10], hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; + int sl = -1, optval; + struct addrinfo hints, *res = NULL, *ressave = NULL; + char portnumber[10], hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; - snprintf(portnumber, sizeof(portnumber), "%u", port); + snprintf(portnumber, sizeof(portnumber), "%u", port); - memset(&hints, 0, sizeof(struct addrinfo)); - /* Use the AI_PASSIVE flag, which indicates we are using this address for a listen() */ - hints.ai_flags = AI_PASSIVE; + memset(&hints, 0, sizeof(struct addrinfo)); + /* Use the AI_PASSIVE flag, which indicates we are using this address for a listen() */ + hints.ai_flags = AI_PASSIVE; #if defined(BSD) - hints.ai_family = AF_INET; + hints.ai_family = AF_INET; #else - hints.ai_family = AF_UNSPEC; + hints.ai_family = AF_UNSPEC; #endif - hints.ai_socktype = SOCK_STREAM; - - optval = getaddrinfo(local ? "localhost" : NULL, portnumber, &hints, &res); - - if (optval != 0) { - motion_log(LOG_ERR, 1, "%s: getaddrinfo() for motion-stream socket failed: %s", - __FUNCTION__, gai_strerror(optval)); - - if (res != NULL) - freeaddrinfo(res); - return -1; - } - - ressave = res; - - while (res) { - /* create socket */ - sl = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - - getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, - sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); - - if (sl >= 0) { - optval = 1; - /* Reuse Address */ - setsockopt(sl, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof( int ) ); - - motion_log(LOG_INFO, 0, "%s: motion-stream testing : %s addr: %s port: %s", - __FUNCTION__, res->ai_family == AF_INET ? "IPV4":"IPV6", hbuf, sbuf); - - if (bind(sl, res->ai_addr, res->ai_addrlen) == 0){ - motion_log(LOG_INFO, 0, "%s: motion-stream Bound : %s addr: %s port: %s", - __FUNCTION__, res->ai_family == AF_INET ? "IPV4":"IPV6", hbuf, sbuf); - break; - } - - motion_log(LOG_ERR, 1, "%s: motion-stream bind() failed, retrying", __FUNCTION__); - close(sl); - sl = -1; - } - motion_log(LOG_ERR, 1, "%s: motion-stream socket failed, retrying", __FUNCTION__); - res = res->ai_next; - } - - freeaddrinfo(ressave); - - if (sl < 0) { - motion_log(LOG_ERR, 1, "%s: motion-stream creating socket/bind ERROR", __FUNCTION__); - return -1; - } - - - if (listen(sl, DEF_MAXWEBQUEUE) == -1) { - motion_log(LOG_ERR, 1, "%s: motion-stream listen() ERROR", __FUNCTION__); - close(sl); - sl = -1; - } - - return sl; + hints.ai_socktype = SOCK_STREAM; + + optval = getaddrinfo(local ? "localhost" : NULL, portnumber, &hints, &res); + + if (optval != 0) { + motion_log(LOG_ERR, 1, "%s: getaddrinfo() for motion-stream socket failed: %s", + __FUNCTION__, gai_strerror(optval)); + + if (res != NULL) + freeaddrinfo(res); + return -1; + } + + ressave = res; + + while (res) { + /* create socket */ + sl = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + + getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, + sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); + + if (sl >= 0) { + optval = 1; + /* Reuse Address */ + setsockopt(sl, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int)); + + motion_log(LOG_INFO, 0, "%s: motion-stream testing : %s addr: %s port: %s", + __FUNCTION__, res->ai_family == AF_INET ? "IPV4":"IPV6", hbuf, sbuf); + + if (bind(sl, res->ai_addr, res->ai_addrlen) == 0) { + motion_log(LOG_INFO, 0, "%s: motion-stream Bound : %s addr: %s port: %s", + __FUNCTION__, res->ai_family == AF_INET ? "IPV4":"IPV6", hbuf, sbuf); + break; + } + + motion_log(LOG_ERR, 1, "%s: motion-stream bind() failed, retrying", __FUNCTION__); + close(sl); + sl = -1; + } + motion_log(LOG_ERR, 1, "%s: motion-stream socket failed, retrying", __FUNCTION__); + res = res->ai_next; + } + + freeaddrinfo(ressave); + + if (sl < 0) { + motion_log(LOG_ERR, 1, "%s: motion-stream creating socket/bind ERROR", __FUNCTION__); + return -1; + } + + + if (listen(sl, DEF_MAXWEBQUEUE) == -1) { + motion_log(LOG_ERR, 1, "%s: motion-stream listen() ERROR", __FUNCTION__); + close(sl); + sl = -1; + } + + return sl; } static int http_acceptsock(int sl) { - int sc; - unsigned long i; - struct sockaddr_storage sin; - socklen_t addrlen = sizeof(sin); - - if ((sc = accept(sl, (struct sockaddr *)&sin, &addrlen)) >= 0) { - i = 1; - ioctl(sc, FIONBIO, &i); - return sc; - } - - motion_log(LOG_ERR, 1, "%s: motion-stream accept()", __FUNCTION__); - - return -1; + int sc; + unsigned long i; + struct sockaddr_storage sin; + socklen_t addrlen = sizeof(sin); + + if ((sc = accept(sl, (struct sockaddr *)&sin, &addrlen)) >= 0) { + i = 1; + ioctl(sc, FIONBIO, &i); + return sc; + } + + motion_log(LOG_ERR, 1, "%s: motion-stream accept()", __FUNCTION__); + + return -1; } @@ -138,98 +138,99 @@ static int http_acceptsock(int sl) */ static void stream_flush(struct stream *list, int *stream_count, int lim) { - int written; /* the number of bytes actually written */ - struct stream *client; /* pointer to the client being served */ - int workdone = 0; /* flag set any time data is successfully - written */ - - client = list->next; - - while (client) { - - /* If data waiting for client, try to send it */ - if (client->tmpbuffer) { - - /* We expect that list->filepos < list->tmpbuffer->size - * should always be true. The check is more for safety, - * in case of trouble is some other part of the code. - * Note that if it is false, the following section will - * clean up. - */ - if (client->filepos < client->tmpbuffer->size) { - - /* Here we are finally ready to write out the - * data. Remember that (because the socket - * has been set non-blocking) we may only - * write out part of the buffer. The var - * 'filepos' contains how much of the buffer - * has already been written. - */ - written = write(client->socket, - client->tmpbuffer->ptr + client->filepos, - client->tmpbuffer->size - client->filepos); - - /* If any data has been written, update the - * data pointer and set the workdone flag - */ - if (written > 0) { - client->filepos += written; - workdone = 1; - } - } else - written = 0; - - /* If we have written the entire buffer to the socket, - * or if there was some error (other than EAGAIN, which - * means the system couldn't take it), this request is - * finished. - */ - if ( (client->filepos >= client->tmpbuffer->size) || - (written < 0 && errno != EAGAIN)) { - /* If no other clients need this buffer, free it */ - if (--client->tmpbuffer->ref <= 0) { - free(client->tmpbuffer->ptr); - free(client->tmpbuffer); - } - - /* Mark this client's buffer as empty */ - client->tmpbuffer = NULL; - client->nr++; - } - - /* If the client is no longer connected, or the total - * number of frames already sent to this client is - * greater than our configuration limit, disconnect - * the client and free the stream struct - */ - if ( (written < 0 && errno != EAGAIN) || - (lim && !client->tmpbuffer && client->nr > lim) ) { - void *tmp; - - close(client->socket); - - if (client->next) - client->next->prev = client->prev; - - client->prev->next = client->next; - tmp = client; - client = client->prev; - free(tmp); - (*stream_count)--; - } - } /* end if (client->tmpbuffer) */ - - /* Step the the next client in the list. If we get to the - * end of the list, check if anything was written during - * that loop; (if so) reset the 'workdone' flag and go back - * to the beginning - */ - client = client->next; - if (!client && workdone) { - client = list->next; - workdone = 0; - } - } /* end while (client) */ + int written; /* the number of bytes actually written */ + struct stream *client; /* pointer to the client being served */ + int workdone = 0; /* flag set any time data is successfully + written */ + + client = list->next; + + while (client) { + + /* If data waiting for client, try to send it */ + if (client->tmpbuffer) { + + /* We expect that list->filepos < list->tmpbuffer->size + * should always be true. The check is more for safety, + * in case of trouble is some other part of the code. + * Note that if it is false, the following section will + * clean up. + */ + if (client->filepos < client->tmpbuffer->size) { + + /* Here we are finally ready to write out the + * data. Remember that (because the socket + * has been set non-blocking) we may only + * write out part of the buffer. The var + * 'filepos' contains how much of the buffer + * has already been written. + */ + written = write(client->socket, + client->tmpbuffer->ptr + client->filepos, + client->tmpbuffer->size - client->filepos); + + /* If any data has been written, update the + * data pointer and set the workdone flag + */ + if (written > 0) { + client->filepos += written; + workdone = 1; + } + } else + written = 0; + + /* If we have written the entire buffer to the socket, + * or if there was some error (other than EAGAIN, which + * means the system couldn't take it), this request is + * finished. + */ + if ((client->filepos >= client->tmpbuffer->size) || + (written < 0 && errno != EAGAIN)) { + /* If no other clients need this buffer, free it */ + if (--client->tmpbuffer->ref <= 0) { + free(client->tmpbuffer->ptr); + free(client->tmpbuffer); + } + + /* Mark this client's buffer as empty */ + client->tmpbuffer = NULL; + client->nr++; + } + + /* If the client is no longer connected, or the total + * number of frames already sent to this client is + * greater than our configuration limit, disconnect + * the client and free the stream struct + */ + if ((written < 0 && errno != EAGAIN) || + (lim && !client->tmpbuffer && client->nr > lim)) { + void *tmp; + + close(client->socket); + + if (client->next) + client->next->prev = client->prev; + + client->prev->next = client->next; + tmp = client; + client = client->prev; + free(tmp); + (*stream_count)--; + } + } /* end if (client->tmpbuffer) */ + + /* Step the the next client in the list. If we get to the + * end of the list, check if anything was written during + * that loop; (if so) reset the 'workdone' flag and go back + * to the beginning + */ + client = client->next; + + if (!client && workdone) { + client = list->next; + workdone = 0; + } + } /* end while (client) */ } /* Routine to create a new "tmpbuffer", which is a common @@ -237,69 +238,70 @@ static void stream_flush(struct stream *list, int *stream_count, int lim) */ static struct stream_buffer *stream_tmpbuffer(int size) { - struct stream_buffer *tmpbuffer = mymalloc(sizeof(struct stream_buffer)); - tmpbuffer->ref = 0; - tmpbuffer->ptr = mymalloc(size); - - return tmpbuffer; + struct stream_buffer *tmpbuffer = mymalloc(sizeof(struct stream_buffer)); + tmpbuffer->ref = 0; + tmpbuffer->ptr = mymalloc(size); + + return tmpbuffer; } static void stream_add_client(struct stream *list, int sc) { - struct stream *new = mymalloc(sizeof(struct stream)); - static const char header[] = "HTTP/1.0 200 OK\r\n" - "Server: Motion/"VERSION"\r\n" - "Connection: close\r\n" - "Max-Age: 0\r\n" - "Expires: 0\r\n" - "Cache-Control: no-cache, private\r\n" - "Pragma: no-cache\r\n" - "Content-Type: multipart/x-mixed-replace; boundary=--BoundaryString\r\n\r\n"; - - memset(new, 0, sizeof(struct stream)); - new->socket = sc; - - if ((new->tmpbuffer = stream_tmpbuffer(sizeof(header))) == NULL) { - motion_log(LOG_ERR, 1, "%s: Error creating tmpbuffer in stream_add_client", __FUNCTION__); - } else { - memcpy(new->tmpbuffer->ptr, header, sizeof(header)-1); - new->tmpbuffer->size = sizeof(header)-1; - } - - new->prev = list; - new->next = list->next; - - if (new->next) - new->next->prev = new; - - list->next = new; + struct stream *new = mymalloc(sizeof(struct stream)); + static const char header[] = "HTTP/1.0 200 OK\r\n" + "Server: Motion/"VERSION"\r\n" + "Connection: close\r\n" + "Max-Age: 0\r\n" + "Expires: 0\r\n" + "Cache-Control: no-cache, private\r\n" + "Pragma: no-cache\r\n" + "Content-Type: multipart/x-mixed-replace; " + "boundary=--BoundaryString\r\n\r\n"; + + memset(new, 0, sizeof(struct stream)); + new->socket = sc; + + if ((new->tmpbuffer = stream_tmpbuffer(sizeof(header))) == NULL) { + motion_log(LOG_ERR, 1, "%s: Error creating tmpbuffer in stream_add_client", __FUNCTION__); + } else { + memcpy(new->tmpbuffer->ptr, header, sizeof(header)-1); + new->tmpbuffer->size = sizeof(header)-1; + } + + new->prev = list; + new->next = list->next; + + if (new->next) + new->next->prev = new; + + list->next = new; } static void stream_add_write(struct stream *list, struct stream_buffer *tmpbuffer, unsigned int fps) { - struct timeval curtimeval; - unsigned long int curtime; - - gettimeofday(&curtimeval, NULL); - curtime = curtimeval.tv_usec + 1000000L * curtimeval.tv_sec; - - while (list->next) { - list = list->next; - - if (list->tmpbuffer == NULL && ((curtime - list->last) >= 1000000L / fps)) { - list->last = curtime; - list->tmpbuffer = tmpbuffer; - tmpbuffer->ref++; - list->filepos = 0; - } - } - - if (tmpbuffer->ref <= 0) { - free(tmpbuffer->ptr); - free(tmpbuffer); - } + struct timeval curtimeval; + unsigned long int curtime; + + gettimeofday(&curtimeval, NULL); + curtime = curtimeval.tv_usec + 1000000L * curtimeval.tv_sec; + + while (list->next) { + list = list->next; + + if (list->tmpbuffer == NULL && ((curtime - list->last) >= 1000000L / fps)) { + list->last = curtime; + list->tmpbuffer = tmpbuffer; + tmpbuffer->ref++; + list->filepos = 0; + } + } + + if (tmpbuffer->ref <= 0) { + free(tmpbuffer->ptr); + free(tmpbuffer); + } } @@ -310,13 +312,13 @@ static void stream_add_write(struct stream *list, struct stream_buffer *tmpbuffe */ static int stream_check_write(struct stream *list) { - while (list->next) { - list = list->next; - - if (list->tmpbuffer == NULL) - return 1; - } - return 0; + while (list->next) { + list = list->next; + + if (list->tmpbuffer == NULL) + return 1; + } + return 0; } @@ -326,49 +328,49 @@ static int stream_check_write(struct stream *list) */ int stream_init(struct context *cnt) { - cnt->stream.socket = http_bindsock(cnt->conf.stream_port, cnt->conf.stream_localhost); - cnt->stream.next = NULL; - cnt->stream.prev = NULL; - return cnt->stream.socket; + cnt->stream.socket = http_bindsock(cnt->conf.stream_port, cnt->conf.stream_localhost); + cnt->stream.next = NULL; + cnt->stream.prev = NULL; + return cnt->stream.socket; } /* This function is called from the motion_loop when it ends * and motion is terminated or restarted */ void stream_stop(struct context *cnt) -{ - struct stream *list; - struct stream *next = cnt->stream.next; - - if (debug_level >= CAMERA_VERBOSE) - motion_log(-1, 0, "%s: Closing motion-stream listen socket" - " & active motion-stream sockets", __FUNCTION__); - else - motion_log(LOG_INFO, 0, "%s: Closing motion-stream listen socket" - " & active motion-stream sockets", __FUNCTION__); - - close(cnt->stream.socket); - cnt->stream.socket = -1; - - while (next) { - list = next; - next = list->next; - - if (list->tmpbuffer) { - free(list->tmpbuffer->ptr); - free(list->tmpbuffer); - } - - close(list->socket); - free(list); - } - - if (debug_level >= CAMERA_VERBOSE) - motion_log(-1, 0, "%s: Closed motion-stream listen socket" - " & active motion-stream sockets", __FUNCTION__); - else - motion_log(LOG_INFO, 0, "%s: Closed motion-stream listen socket" - " & active motion-stream sockets", __FUNCTION__); +{ + struct stream *list; + struct stream *next = cnt->stream.next; + + if (debug_level >= CAMERA_VERBOSE) + motion_log(-1, 0, "%s: Closing motion-stream listen socket" + " & active motion-stream sockets", __FUNCTION__); + else + motion_log(LOG_INFO, 0, "%s: Closing motion-stream listen socket" + " & active motion-stream sockets", __FUNCTION__); + + close(cnt->stream.socket); + cnt->stream.socket = -1; + + while (next) { + list = next; + next = list->next; + + if (list->tmpbuffer) { + free(list->tmpbuffer->ptr); + free(list->tmpbuffer); + } + + close(list->socket); + free(list); + } + + if (debug_level >= CAMERA_VERBOSE) + motion_log(-1, 0, "%s: Closed motion-stream listen socket" + " & active motion-stream sockets", __FUNCTION__); + else + motion_log(LOG_INFO, 0, "%s: Closed motion-stream listen socket" + " & active motion-stream sockets", __FUNCTION__); } /* stream_put is the starting point of the stream loop. It is called from @@ -387,101 +389,101 @@ void stream_stop(struct context *cnt) */ void stream_put(struct context *cnt, unsigned char *image) { - struct timeval timeout; - struct stream_buffer *tmpbuffer; - fd_set fdread; - int sl = cnt->stream.socket; - int sc; - /* the following string has an extra 16 chars at end for length */ - const char jpeghead[] = "--BoundaryString\r\n" - "Content-type: image/jpeg\r\n" - "Content-Length: "; - int headlength = sizeof(jpeghead) - 1; /* don't include terminator */ - char len[20]; /* will be used for sprintf, must be >= 16 */ - - /* timeout struct used to timeout the time we wait for a client - * and we do not wait at all - */ - timeout.tv_sec = 0; - timeout.tv_usec = 0; - FD_ZERO(&fdread); - FD_SET(cnt->stream.socket, &fdread); - - /* If we have not reached the max number of allowed clients per - * thread we will check to see if new clients are waiting to connect. - * If this is the case we add the client as a new stream struct and - * add this to the end of the chain of stream structs that are linked - * to each other. - */ - if ((cnt->stream_count < DEF_MAXSTREAMS) && - (select(sl+1, &fdread, NULL, NULL, &timeout)>0)) { - sc = http_acceptsock(sl); - stream_add_client(&cnt->stream, sc); - cnt->stream_count++; - } - - /* call flush to send any previous partial-sends which are waiting */ - stream_flush(&cnt->stream, &cnt->stream_count, cnt->conf.stream_limit); - - /* Check if any clients have available buffers */ - if (stream_check_write(&cnt->stream)) { - /* yes - create a new tmpbuffer for current image. - * Note that this should create a buffer which is *much* larger - * than necessary, but it is difficult to estimate the - * minimum size actually required. - */ - tmpbuffer = stream_tmpbuffer(cnt->imgs.size); - - /* check if allocation went ok */ - if (tmpbuffer) { - int imgsize; - - /* We need a pointer that points to the picture buffer - * just after the mjpeg header. We create a working pointer wptr - * to be used in the call to put_picture_memory which we can change - * and leave tmpbuffer->ptr intact. - */ - unsigned char *wptr = tmpbuffer->ptr; - - /* For web protocol, our image needs to be preceded - * with a little HTTP, so we put that into the buffer - * first. - */ - memcpy(wptr, jpeghead, headlength); - - /* update our working pointer to point past header */ - wptr += headlength; - - /* create a jpeg image and place into tmpbuffer */ - tmpbuffer->size = put_picture_memory(cnt, wptr, cnt->imgs.size, image, - cnt->conf.stream_quality); - - /* fill in the image length into the header */ - imgsize = sprintf(len, "%9ld\r\n\r\n", tmpbuffer->size); - memcpy(wptr - imgsize, len, imgsize); - - /* append a CRLF for good measure */ - memcpy(wptr + tmpbuffer->size, "\r\n", 2); - - /* now adjust tmpbuffer->size to reflect the - * header at the beginning and the extra CRLF - * at the end. - */ - tmpbuffer->size += headlength + 2; - - /* and finally put this buffer to all clients with - * no outstanding data from previous frames. - */ - stream_add_write(&cnt->stream, tmpbuffer, cnt->conf.stream_maxrate); - } else { - motion_log(LOG_ERR, 1, "%s: Error creating tmpbuffer", __FUNCTION__); - } - } - - /* Now we call flush again. This time (assuming some clients were - * ready for the new frame) the new data will be written out. - */ - stream_flush(&cnt->stream, &cnt->stream_count, cnt->conf.stream_limit); - - return; + struct timeval timeout; + struct stream_buffer *tmpbuffer; + fd_set fdread; + int sl = cnt->stream.socket; + int sc; + /* the following string has an extra 16 chars at end for length */ + const char jpeghead[] = "--BoundaryString\r\n" + "Content-type: image/jpeg\r\n" + "Content-Length: "; + int headlength = sizeof(jpeghead) - 1; /* don't include terminator */ + char len[20]; /* will be used for sprintf, must be >= 16 */ + + /* timeout struct used to timeout the time we wait for a client + * and we do not wait at all + */ + timeout.tv_sec = 0; + timeout.tv_usec = 0; + FD_ZERO(&fdread); + FD_SET(cnt->stream.socket, &fdread); + + /* If we have not reached the max number of allowed clients per + * thread we will check to see if new clients are waiting to connect. + * If this is the case we add the client as a new stream struct and + * add this to the end of the chain of stream structs that are linked + * to each other. + */ + if ((cnt->stream_count < DEF_MAXSTREAMS) && + (select(sl+1, &fdread, NULL, NULL, &timeout)>0)) { + sc = http_acceptsock(sl); + stream_add_client(&cnt->stream, sc); + cnt->stream_count++; + } + + /* call flush to send any previous partial-sends which are waiting */ + stream_flush(&cnt->stream, &cnt->stream_count, cnt->conf.stream_limit); + + /* Check if any clients have available buffers */ + if (stream_check_write(&cnt->stream)) { + /* yes - create a new tmpbuffer for current image. + * Note that this should create a buffer which is *much* larger + * than necessary, but it is difficult to estimate the + * minimum size actually required. + */ + tmpbuffer = stream_tmpbuffer(cnt->imgs.size); + + /* check if allocation went ok */ + if (tmpbuffer) { + int imgsize; + + /* We need a pointer that points to the picture buffer + * just after the mjpeg header. We create a working pointer wptr + * to be used in the call to put_picture_memory which we can change + * and leave tmpbuffer->ptr intact. + */ + unsigned char *wptr = tmpbuffer->ptr; + + /* For web protocol, our image needs to be preceded + * with a little HTTP, so we put that into the buffer + * first. + */ + memcpy(wptr, jpeghead, headlength); + + /* update our working pointer to point past header */ + wptr += headlength; + + /* create a jpeg image and place into tmpbuffer */ + tmpbuffer->size = put_picture_memory(cnt, wptr, cnt->imgs.size, image, + cnt->conf.stream_quality); + + /* fill in the image length into the header */ + imgsize = sprintf(len, "%9ld\r\n\r\n", tmpbuffer->size); + memcpy(wptr - imgsize, len, imgsize); + + /* append a CRLF for good measure */ + memcpy(wptr + tmpbuffer->size, "\r\n", 2); + + /* now adjust tmpbuffer->size to reflect the + * header at the beginning and the extra CRLF + * at the end. + */ + tmpbuffer->size += headlength + 2; + + /* and finally put this buffer to all clients with + * no outstanding data from previous frames. + */ + stream_add_write(&cnt->stream, tmpbuffer, cnt->conf.stream_maxrate); + } else { + motion_log(LOG_ERR, 1, "%s: Error creating tmpbuffer", __FUNCTION__); + } + } + + /* Now we call flush again. This time (assuming some clients were + * ready for the new frame) the new data will be written out. + */ + stream_flush(&cnt->stream, &cnt->stream_count, cnt->conf.stream_limit); + + return; } diff --git a/track.c b/track.c index 1372034..28d0954 100644 --- a/track.c +++ b/track.c @@ -1,9 +1,9 @@ -/* track.c +/* track.c * - * Experimental motion tracking. + * Experimental motion tracking. * - * Copyright 2000, Jeroen Vreeken - * This program is published under the GNU Public license + * Copyright 2000, Jeroen Vreeken + * This program is published under the GNU Public license */ #include @@ -15,25 +15,25 @@ struct trackoptions track_template = { - dev: -1, /* dev open */ - port: NULL, /* char *port */ - motorx: 0, /* int motorx */ - motory: 0, /* int motory */ - maxx: 0, /* int maxx; */ - maxy: 0, /* int maxy; */ - minx: 0, /* int minx; */ - miny: 0, /* int miny; */ - homex: 128, /* int homex; */ - homey: 128, /* int homey; */ - motorx_reverse: 0, /* int reversed x servo; */ - motory_reverse: 0, /* int reversed y servo; */ - speed: TRACK_SPEED, /* speed */ - stepsize: TRACK_STEPSIZE, /* stepsize */ - active: 0, /* auto tracking active */ - minmaxfound: 0, /* flag for minmax values stored for pwc based camera */ - step_angle_x: 10, /* UVC step angle in degrees X-axis that camera moves during auto tracking */ - step_angle_y: 10, /* UVC step angle in degrees Y-axis that camera moves during auto tracking */ - move_wait: 10 /* number of frames to disable motion detection after camera moving */ + dev: -1, /* dev open */ + port: NULL, /* char *port */ + motorx: 0, /* int motorx */ + motory: 0, /* int motory */ + maxx: 0, /* int maxx; */ + maxy: 0, /* int maxy; */ + minx: 0, /* int minx; */ + miny: 0, /* int miny; */ + homex: 128, /* int homex; */ + homey: 128, /* int homey; */ + motorx_reverse: 0, /* int reversed x servo; */ + motory_reverse: 0, /* int reversed y servo; */ + speed: TRACK_SPEED, /* speed */ + stepsize: TRACK_STEPSIZE, /* stepsize */ + active: 0, /* auto tracking active */ + minmaxfound: 0, /* flag for minmax values stored for pwc based camera */ + step_angle_x: 10, /* UVC step angle in degrees X-axis that camera moves during auto tracking */ + step_angle_y: 10, /* UVC step angle in degrees Y-axis that camera moves during auto tracking */ + move_wait: 10 /* number of frames to disable motion detection after camera moving */ }; @@ -67,36 +67,37 @@ static unsigned short int uvc_move(struct context *cnt, int dev, struct coord *c unsigned short int track_center(struct context *cnt, int dev ATTRIBUTE_UNUSED, unsigned short int manual, int xoff, int yoff) { - if (!manual && !cnt->track.active) - return 0; - if (cnt->track.type == TRACK_TYPE_STEPPER) { - unsigned short int ret; - ret = stepper_center(cnt, xoff, yoff); - if (!ret) { - motion_log(LOG_ERR, 1, "%s: internal error", __FUNCTION__); - return 0; - } - else return ret; - } else if (cnt->track.type == TRACK_TYPE_SERVO) { - return servo_center(cnt, xoff, yoff); - } -#ifndef WITHOUT_V4L - else if (cnt->track.type == TRACK_TYPE_PWC) - return lqos_center(cnt, dev, xoff, yoff); + if (!manual && !cnt->track.active) + return 0; + + if (cnt->track.type == TRACK_TYPE_STEPPER) { + unsigned short int ret; + ret = stepper_center(cnt, xoff, yoff); + if (!ret) { + motion_log(LOG_ERR, 1, "%s: internal error", __FUNCTION__); + return 0; + } + else return ret; + } else if (cnt->track.type == TRACK_TYPE_SERVO) { + return servo_center(cnt, xoff, yoff); + } +#ifndef WITHOUT_V4L + else if (cnt->track.type == TRACK_TYPE_PWC) + return lqos_center(cnt, dev, xoff, yoff); #ifdef MOTION_V4L2 - else if (cnt->track.type == TRACK_TYPE_UVC) - return uvc_center(cnt, dev, xoff, yoff); + else if (cnt->track.type == TRACK_TYPE_UVC) + return uvc_center(cnt, dev, xoff, yoff); #endif /* MOTION_V4L2 */ #endif /* WITHOUT_V4L */ - else if (cnt->track.type == TRACK_TYPE_IOMOJO) - return iomojo_center(cnt, xoff, yoff); - else if (cnt->track.type == TRACK_TYPE_GENERIC) - return 10; // FIX ME. I chose to return something reasonable. + else if (cnt->track.type == TRACK_TYPE_IOMOJO) + return iomojo_center(cnt, xoff, yoff); + else if (cnt->track.type == TRACK_TYPE_GENERIC) + return 10; // FIX ME. I chose to return something reasonable. - motion_log(LOG_ERR, 1, "%s: internal error, %hu is not a known track-type", - __FUNCTION__, cnt->track.type); + motion_log(LOG_ERR, 1, "%s: internal error, %hu is not a known track-type", + __FUNCTION__, cnt->track.type); - return 0; + return 0; } /* Add a call to your functions here: */ @@ -104,182 +105,185 @@ unsigned short int track_move(struct context *cnt, int dev, struct coord *cent, unsigned short int manual) { - if (!manual && !cnt->track.active) - return 0; - if (cnt->track.type == TRACK_TYPE_STEPPER) - return stepper_move(cnt, cent, imgs); - else if (cnt->track.type == TRACK_TYPE_SERVO) - return servo_move(cnt, cent, imgs, manual); + if (!manual && !cnt->track.active) + return 0; + + if (cnt->track.type == TRACK_TYPE_STEPPER) + return stepper_move(cnt, cent, imgs); + else if (cnt->track.type == TRACK_TYPE_SERVO) + return servo_move(cnt, cent, imgs, manual); #ifndef WITHOUT_V4L - else if (cnt->track.type == TRACK_TYPE_PWC) - return lqos_move(cnt, dev, cent, imgs, manual); + else if (cnt->track.type == TRACK_TYPE_PWC) + return lqos_move(cnt, dev, cent, imgs, manual); #ifdef MOTION_V4L2 - else if (cnt->track.type == TRACK_TYPE_UVC) - return uvc_move(cnt, dev, cent, imgs, manual); + else if (cnt->track.type == TRACK_TYPE_UVC) + return uvc_move(cnt, dev, cent, imgs, manual); #endif /* MOTION_V4L2 */ #endif /* WITHOUT_V4L */ - else if (cnt->track.type == TRACK_TYPE_IOMOJO) - return iomojo_move(cnt, dev, cent, imgs); - else if (cnt->track.type == TRACK_TYPE_GENERIC) - return cnt->track.move_wait; // FIX ME. I chose to return something reasonable. + else if (cnt->track.type == TRACK_TYPE_IOMOJO) + return iomojo_move(cnt, dev, cent, imgs); + else if (cnt->track.type == TRACK_TYPE_GENERIC) + return cnt->track.move_wait; // FIX ME. I chose to return something reasonable. - motion_log(LOG_ERR, 1, "%s: internal error, %hu is not a known track-type", - __FUNCTION__, cnt->track.type); + motion_log(LOG_ERR, 1, "%s: internal error, %hu is not a known track-type", + __FUNCTION__, cnt->track.type); - return 0; + return 0; } /****************************************************************************** - Stepper motor on serial port - http://www.lavrsen.dk/twiki/bin/view/Motion/MotionTracking - http://www.lavrsen.dk/twiki/bin/view/Motion/MotionTrackerAPI + Stepper motor on serial port + http://www.lavrsen.dk/twiki/bin/view/Motion/MotionTracking + http://www.lavrsen.dk/twiki/bin/view/Motion/MotionTrackerAPI ******************************************************************************/ static unsigned short int stepper_command(struct context *cnt, unsigned short int motor, unsigned short int command, unsigned short int data) { - char buffer[3]; - time_t timeout = time(NULL); - - buffer[0] = motor; - buffer[1] = command; - buffer[2] = data; - - if (write(cnt->track.dev, buffer, 3) != 3){ - motion_log(LOG_ERR, 1, "%s: port %s dev fd %i, motor %hu command %hu data %hu", - __FUNCTION__, cnt->track.port, cnt->track.dev, motor, command, data); - return 0; - } - - while (read(cnt->track.dev, buffer, 1) != 1 && time(NULL) < timeout+1); - if (time(NULL) >= timeout+2) { - motion_log(LOG_ERR, 1, "%s: Status byte timeout!", __FUNCTION__); - return 0; - } - - return buffer[0]; + char buffer[3]; + time_t timeout = time(NULL); + + buffer[0] = motor; + buffer[1] = command; + buffer[2] = data; + + if (write(cnt->track.dev, buffer, 3) != 3) { + motion_log(LOG_ERR, 1, "%s: port %s dev fd %i, motor %hu command %hu data %hu", + __FUNCTION__, cnt->track.port, cnt->track.dev, motor, command, data); + return 0; + } + + while (read(cnt->track.dev, buffer, 1) != 1 && time(NULL) < timeout+1); + + if (time(NULL) >= timeout+2) { + motion_log(LOG_ERR, 1, "%s: Status byte timeout!", __FUNCTION__); + return 0; + } + + return buffer[0]; } static unsigned short int stepper_status(struct context *cnt, unsigned short int motor) { - return stepper_command(cnt, motor, STEPPER_COMMAND_STATUS, 0); + return stepper_command(cnt, motor, STEPPER_COMMAND_STATUS, 0); } static unsigned short int stepper_center(struct context *cnt, int x_offset, int y_offset) { - struct termios adtio; - - if (cnt->track.dev < 0) { - motion_log(LOG_INFO, 0, "%s: Try to open serial device %s", __FUNCTION__, cnt->track.port); - - if ((cnt->track.dev = open(cnt->track.port, O_RDWR | O_NOCTTY)) < 0) { - motion_log(LOG_ERR, 1, "%s: Unable to open serial device %s", - __FUNCTION__, cnt->track.port); - return 0; - } - - bzero (&adtio, sizeof(adtio)); - adtio.c_cflag= STEPPER_BAUDRATE | CS8 | CLOCAL | CREAD; - adtio.c_iflag= IGNPAR; - adtio.c_oflag= 0; - adtio.c_lflag= 0; /* non-canon, no echo */ - adtio.c_cc[VTIME] = 0; /* timer unused */ - adtio.c_cc[VMIN] = 0; /* blocking read until 1 char */ - tcflush (cnt->track.dev, TCIFLUSH); - - if (tcsetattr(cnt->track.dev, TCSANOW, &adtio) < 0) { - motion_log(LOG_ERR, 1, "%s: Unable to initialize serial device %s", - __FUNCTION__, cnt->track.port); - cnt->track.dev = -1; - return 0; - } - motion_log(LOG_INFO, 0, "$s: Opened serial device %s and initialize, fd %i", - __FUNCTION__, cnt->track.port, cnt->track.dev); - } - - /* x-axis */ - - stepper_command(cnt, cnt->track.motorx, STEPPER_COMMAND_SPEED, cnt->track.speed); - stepper_command(cnt, cnt->track.motorx, STEPPER_COMMAND_LEFT_N, cnt->track.maxx); - - while (stepper_status(cnt, cnt->track.motorx) & STEPPER_STATUS_LEFT); - - stepper_command(cnt, cnt->track.motorx, STEPPER_COMMAND_RIGHT_N, - cnt->track.maxx / 2 + x_offset * cnt->track.stepsize); - - while (stepper_status(cnt, cnt->track.motorx) & STEPPER_STATUS_RIGHT); - - /* y-axis */ - - stepper_command(cnt, cnt->track.motory, STEPPER_COMMAND_SPEED, cnt->track.speed); - stepper_command(cnt, cnt->track.motory, STEPPER_COMMAND_UP_N, cnt->track.maxy); - - while (stepper_status(cnt, cnt->track.motory) & STEPPER_STATUS_UP) - - stepper_command(cnt, cnt->track.motory, STEPPER_COMMAND_DOWN_N, - cnt->track.maxy / 2 + y_offset * cnt->track.stepsize); - - while (stepper_status(cnt, cnt->track.motory) & STEPPER_STATUS_DOWN); - - return cnt->track.move_wait; + struct termios adtio; + + if (cnt->track.dev < 0) { + motion_log(LOG_INFO, 0, "%s: Try to open serial device %s", __FUNCTION__, cnt->track.port); + + if ((cnt->track.dev = open(cnt->track.port, O_RDWR | O_NOCTTY)) < 0) { + motion_log(LOG_ERR, 1, "%s: Unable to open serial device %s", + __FUNCTION__, cnt->track.port); + return 0; + } + + bzero (&adtio, sizeof(adtio)); + adtio.c_cflag= STEPPER_BAUDRATE | CS8 | CLOCAL | CREAD; + adtio.c_iflag= IGNPAR; + adtio.c_oflag= 0; + adtio.c_lflag= 0; /* non-canon, no echo */ + adtio.c_cc[VTIME] = 0; /* timer unused */ + adtio.c_cc[VMIN] = 0; /* blocking read until 1 char */ + tcflush (cnt->track.dev, TCIFLUSH); + + if (tcsetattr(cnt->track.dev, TCSANOW, &adtio) < 0) { + motion_log(LOG_ERR, 1, "%s: Unable to initialize serial device %s", + __FUNCTION__, cnt->track.port); + cnt->track.dev = -1; + return 0; + } + motion_log(LOG_INFO, 0, "$s: Opened serial device %s and initialize, fd %i", + __FUNCTION__, cnt->track.port, cnt->track.dev); + } + + /* x-axis */ + + stepper_command(cnt, cnt->track.motorx, STEPPER_COMMAND_SPEED, cnt->track.speed); + stepper_command(cnt, cnt->track.motorx, STEPPER_COMMAND_LEFT_N, cnt->track.maxx); + + while (stepper_status(cnt, cnt->track.motorx) & STEPPER_STATUS_LEFT); + + stepper_command(cnt, cnt->track.motorx, STEPPER_COMMAND_RIGHT_N, + cnt->track.maxx / 2 + x_offset * cnt->track.stepsize); + + while (stepper_status(cnt, cnt->track.motorx) & STEPPER_STATUS_RIGHT); + + /* y-axis */ + + stepper_command(cnt, cnt->track.motory, STEPPER_COMMAND_SPEED, cnt->track.speed); + stepper_command(cnt, cnt->track.motory, STEPPER_COMMAND_UP_N, cnt->track.maxy); + + while (stepper_status(cnt, cnt->track.motory) & STEPPER_STATUS_UP) + + stepper_command(cnt, cnt->track.motory, STEPPER_COMMAND_DOWN_N, + cnt->track.maxy / 2 + y_offset * cnt->track.stepsize); + + while (stepper_status(cnt, cnt->track.motory) & STEPPER_STATUS_DOWN); + + return cnt->track.move_wait; } static unsigned short int stepper_move(struct context *cnt, struct coord *cent, struct images *imgs) { - unsigned short int command = 0, data = 0; - - if (cnt->track.dev < 0) { - motion_log(LOG_INFO, 0, "%s: No device %s started yet , trying stepper_center()", - __FUNCTION__, cnt->track.port); - - if (!stepper_center(cnt, 0, 0)) { - motion_log(LOG_ERR, 1, "%s: failed to initialize stepper device on %s , fd [%i].", - __FUNCTION__, cnt->track.port, cnt->track.dev); - return 0; - } - - motion_log(LOG_INFO, 0, "%s: succeed , device started %s , fd [%i]", - __FUNCTION__, cnt->track.port, cnt->track.dev); - } - - /* x-axis */ - - if (cent->x < imgs->width / 2) { - command = STEPPER_COMMAND_LEFT_N; - data = imgs->width / 2 - cent->x; - } - - if (cent->x > imgs->width / 2) { - command = STEPPER_COMMAND_RIGHT_N; - data = cent->x - imgs->width / 2; - } - - data = data * cnt->track.stepsize / imgs->width; - - if (data) stepper_command(cnt, cnt->track.motorx, command, data); - - /* y-axis */ - - if (cent->y < imgs->height / 2) { - command = STEPPER_COMMAND_UP_N; - data = imgs->height / 2 - cent->y; - } - - if (cent->y > imgs->height / 2) { - command = STEPPER_COMMAND_DOWN_N; - data = cent->y - imgs->height / 2; - } - - data = data * cnt->track.stepsize / imgs->height; - - if (data) stepper_command(cnt, cnt->track.motory, command, data); - - - return cnt->track.move_wait; + unsigned short int command = 0, data = 0; + + if (cnt->track.dev < 0) { + motion_log(LOG_INFO, 0, "%s: No device %s started yet , trying stepper_center()", + __FUNCTION__, cnt->track.port); + + if (!stepper_center(cnt, 0, 0)) { + motion_log(LOG_ERR, 1, "%s: failed to initialize stepper device on %s , fd [%i].", + __FUNCTION__, cnt->track.port, cnt->track.dev); + return 0; + } + + motion_log(LOG_INFO, 0, "%s: succeed , device started %s , fd [%i]", + __FUNCTION__, cnt->track.port, cnt->track.dev); + } + + /* x-axis */ + + if (cent->x < imgs->width / 2) { + command = STEPPER_COMMAND_LEFT_N; + data = imgs->width / 2 - cent->x; + } + + if (cent->x > imgs->width / 2) { + command = STEPPER_COMMAND_RIGHT_N; + data = cent->x - imgs->width / 2; + } + + data = data * cnt->track.stepsize / imgs->width; + + if (data) stepper_command(cnt, cnt->track.motorx, command, data); + + /* y-axis */ + + if (cent->y < imgs->height / 2) { + command = STEPPER_COMMAND_UP_N; + data = imgs->height / 2 - cent->y; + } + + if (cent->y > imgs->height / 2) { + command = STEPPER_COMMAND_DOWN_N; + data = cent->y - imgs->height / 2; + } + + data = data * cnt->track.stepsize / imgs->height; + + if (data) + stepper_command(cnt, cnt->track.motory, command, data); + + + return cnt->track.move_wait; } @@ -291,81 +295,81 @@ static unsigned short int stepper_move(struct context *cnt, struct coord *cent, static int servo_open(struct context *cnt) { - struct termios adtio; - - if ((cnt->track.dev = open(cnt->track.port, O_RDWR | O_NOCTTY)) < 0) { - motion_log(LOG_ERR, 1, "%s: Unable to open serial device %s", - __FUNCTION__, cnt->track.port); - return 0; - } - - bzero (&adtio, sizeof(adtio)); - adtio.c_cflag= SERVO_BAUDRATE | CS8 | CLOCAL | CREAD; - adtio.c_iflag= IGNPAR; - adtio.c_oflag= 0; - adtio.c_lflag= 0; /* non-canon, no echo */ - adtio.c_cc[VTIME] = 0; /* timer unused */ - adtio.c_cc[VMIN] = 0; /* blocking read until 1 char */ - tcflush (cnt->track.dev, TCIFLUSH); - - if (tcsetattr(cnt->track.dev, TCSANOW, &adtio) < 0) { - motion_log(LOG_INFO, 0, "%s: Unable to initialize serial device %s", - __FUNCTION__, cnt->track.port); - cnt->track.dev = -1; - return 0; - } - - motion_log(LOG_INFO, 0, "%s: Opened serial device %s and initialize, fd %i", - __FUNCTION__, cnt->track.port, cnt->track.dev); - - return 1; + struct termios adtio; + + if ((cnt->track.dev = open(cnt->track.port, O_RDWR | O_NOCTTY)) < 0) { + motion_log(LOG_ERR, 1, "%s: Unable to open serial device %s", + __FUNCTION__, cnt->track.port); + return 0; + } + + bzero (&adtio, sizeof(adtio)); + adtio.c_cflag= SERVO_BAUDRATE | CS8 | CLOCAL | CREAD; + adtio.c_iflag= IGNPAR; + adtio.c_oflag= 0; + adtio.c_lflag= 0; /* non-canon, no echo */ + adtio.c_cc[VTIME] = 0; /* timer unused */ + adtio.c_cc[VMIN] = 0; /* blocking read until 1 char */ + tcflush (cnt->track.dev, TCIFLUSH); + + if (tcsetattr(cnt->track.dev, TCSANOW, &adtio) < 0) { + motion_log(LOG_INFO, 0, "%s: Unable to initialize serial device %s", + __FUNCTION__, cnt->track.port); + cnt->track.dev = -1; + return 0; + } + + motion_log(LOG_INFO, 0, "%s: Opened serial device %s and initialize, fd %i", + __FUNCTION__, cnt->track.port, cnt->track.dev); + + return 1; } static unsigned short int servo_command(struct context *cnt, unsigned short int motor, unsigned short int command, unsigned short int data) { - unsigned char buffer[3]; - time_t timeout = time(NULL); + unsigned char buffer[3]; + time_t timeout = time(NULL); - buffer[0] = motor; - buffer[1] = command; - buffer[2] = data; + buffer[0] = motor; + buffer[1] = command; + buffer[2] = data; - if (debug_level >= TRACK_DEBUG) { - motion_log(LOG_DEBUG, 0, "%s: SENDS port %s dev fd %i, motor %hu command %hu data %hu", - __FUNCTION__, cnt->track.port, cnt->track.dev, buffer[0], buffer[1], buffer[2]); - } + if (debug_level >= TRACK_DEBUG) { + motion_log(LOG_DEBUG, 0, "%s: SENDS port %s dev fd %i, motor %hu command %hu data %hu", + __FUNCTION__, cnt->track.port, cnt->track.dev, buffer[0], buffer[1], buffer[2]); + } - if (write(cnt->track.dev, buffer, 3) != 3){ - motion_log(LOG_INFO, 0, "%s: port %s dev fd %i, motor %hu command %hu data %hu", - __FUNCTION__, cnt->track.port, cnt->track.dev, motor, command, data); - return 0; - } + if (write(cnt->track.dev, buffer, 3) != 3) { + motion_log(LOG_INFO, 0, "%s: port %s dev fd %i, motor %hu command %hu data %hu", + __FUNCTION__, cnt->track.port, cnt->track.dev, motor, command, data); + return 0; + } - while (read(cnt->track.dev, buffer, 1) != 1 && time(NULL) < timeout+1); - - if (time(NULL) >= timeout+2) { - motion_log(LOG_ERR, 0, "%s: Status byte timeout!", __FUNCTION__); - return 0; - } + while (read(cnt->track.dev, buffer, 1) != 1 && time(NULL) < timeout+1); + + if (time(NULL) >= timeout+2) { + motion_log(LOG_ERR, 0, "%s: Status byte timeout!", __FUNCTION__); + return 0; + } - if (debug_level >= TRACK_DEBUG) - motion_log(LOG_DEBUG, 0, "%s: Command return %d", __FUNCTION__, buffer[0]); - + if (debug_level >= TRACK_DEBUG) + motion_log(LOG_DEBUG, 0, "%s: Command return %d", __FUNCTION__, buffer[0]); + - return buffer[0]; + return buffer[0]; } static unsigned short int servo_position(struct context *cnt, short unsigned int motor) { - unsigned short int ret = 0; + unsigned short int ret = 0; - ret = servo_command(cnt, motor, SERVO_COMMAND_POSITION, 0); + ret = servo_command(cnt, motor, SERVO_COMMAND_POSITION, 0); - return ret; + return ret; } @@ -379,183 +383,183 @@ static unsigned short int servo_position(struct context *cnt, short unsigned int static unsigned short int servo_move(struct context *cnt, struct coord *cent, struct images *imgs, short unsigned int manual) { - unsigned short int command = 0; - unsigned short int data = 0; - - /* If device is not open yet , open and center */ - if (cnt->track.dev < 0) { - if (!servo_center(cnt, 0, 0)) { - motion_log(LOG_INFO, 0, "%s: Problem opening servo!", __FUNCTION__); - return 0; - } - } - - if (debug_level >= TRACK_DEBUG) - motion_log(LOG_DEBUG, 0, "%s: cent->x %d, cent->y %d, reversex %d," - "reversey %d manual %d", __FUNCTION__, cent->x , cent->y, - cnt->track.motorx_reverse, cnt->track.motory_reverse, manual); - - - if (manual) { - int offset; - int position; - - if (cent->x) { - position = servo_position(cnt, cnt->track.motorx); - offset = cent->x * cnt->track.stepsize; - - - if ( (cnt->track.motorx_reverse && (offset > 0)) || - (!cnt->track.motorx_reverse && (offset < 0)) ) - command = SERVO_COMMAND_LEFT_N; - else - command = SERVO_COMMAND_RIGHT_N; - - data = abs(offset); - - if ((data + position > cnt->track.maxx) || (position - offset < cnt->track.minx)) { - motion_log(LOG_INFO, 0, "%s: x %d value out of range! (%d - %d)", - __FUNCTION__, data, cnt->track.minx, cnt->track.maxx); - return 0; - } - - - /* Set Speed , TODO : it should be done only when speed changes */ - servo_command(cnt, cnt->track.motorx, SERVO_COMMAND_SPEED, cnt->track.speed); - servo_command(cnt, cnt->track.motorx, command, data); - } - - - if (cent->y) { - position = servo_position(cnt, cnt->track.motory); - offset = cent->y * cnt->track.stepsize; - - if ( (cnt->track.motory_reverse && (offset > 0)) || - (!cnt->track.motory_reverse && (offset < 0)) ) - command = SERVO_COMMAND_UP_N; - else - command = SERVO_COMMAND_DOWN_N; - - data = abs(offset); - - if ((data + position > cnt->track.maxy) || (position - offset < cnt->track.miny)) { - motion_log(LOG_INFO, 0, "%s: y %d value out of range! (%d - %d)", - __FUNCTION__, data, cnt->track.miny, cnt->track.maxy); - return 0; - } - - - /* Set Speed , TODO : it should be done only when speed changes */ - servo_command(cnt, cnt->track.motory, SERVO_COMMAND_SPEED, cnt->track.speed); - servo_command(cnt, cnt->track.motory, command, data); - } - - } else { - int position; - - /***** x-axis *****/ - - /* Move left */ - if (cent->x < imgs->width / 2) { - if (cnt->track.motorx_reverse) - command = SERVO_COMMAND_RIGHT_N; - else - command = SERVO_COMMAND_LEFT_N; - data = imgs->width / 2 - cent->x; - } - - /* Move right */ - if (cent->x > imgs->width / 2) { - if (cnt->track.motorx_reverse) - command = SERVO_COMMAND_LEFT_N; - else - command = SERVO_COMMAND_RIGHT_N; - data = cent->x - imgs->width / 2; - } - - - if (debug_level >= TRACK_DEBUG) - motion_log(LOG_DEBUG, 0, "%s: X offset %d", __FUNCTION__, data); - - data = data * cnt->track.stepsize / imgs->width; - - if (data && command) { - - // TODO: need to get position to avoid overflow limits - position = servo_position(cnt, cnt->track.motorx); - - if ((position + data > cnt->track.maxx) || (position - data < cnt->track.minx)) { - motion_log(LOG_INFO, 0, "%s: x %d value out of range! (%d - %d)", - __FUNCTION__, data, cnt->track.minx, cnt->track.maxx); - return 0; - } - - /* Set Speed , TODO : it should be done only when speed changes */ - - servo_command(cnt, cnt->track.motorx, SERVO_COMMAND_SPEED, cnt->track.speed); - servo_command(cnt, cnt->track.motorx, command, data); - - if (debug_level >= TRACK_DEBUG) - motion_log(LOG_DEBUG, 0, "%s: X cent->x %d, cent->y %d, reversex %d," - "reversey %d motorx %d data %d command %d", __FUNCTION__, - cent->x, cent->y, cnt->track.motorx_reverse, - cnt->track.motory_reverse, cnt->track.motorx, data, command); - } - - /***** y-axis *****/ - - /* Move down */ - if (cent->y < imgs->height / 2) { - if (cnt->track.motory_reverse) - command = SERVO_COMMAND_UP_N; - else - command = SERVO_COMMAND_DOWN_N; - data = imgs->height / 2 - cent->y; - } - - /* Move up */ - if (cent->y > imgs->height / 2) { - if (cnt->track.motory_reverse) - command = SERVO_COMMAND_DOWN_N; - else - command = SERVO_COMMAND_UP_N; - data = cent->y - imgs->height / 2; - } - - if (debug_level >= TRACK_DEBUG) - motion_log(LOG_DEBUG, 0, "%s: Y offset %d", __FUNCTION__, data); - - data = data * cnt->track.stepsize / imgs->height; - - if (data && command) { - - // TODO: need to get position to avoid overflow limits - position = servo_position(cnt, cnt->track.motory); - - if ((position + data > cnt->track.maxy) || (position - data < cnt->track.miny)) { - motion_log(LOG_INFO, 0, "%s: y %d value out of range! (%d - %d)", - __FUNCTION__, data, cnt->track.miny, cnt->track.maxy); - return 0; - } - - /* Set Speed , TODO : it should be done only when speed changes */ - servo_command(cnt, cnt->track.motory, SERVO_COMMAND_SPEED, cnt->track.speed); - servo_command(cnt, cnt->track.motory, command, data); - - if (debug_level >= TRACK_DEBUG) - motion_log(LOG_DEBUG, 0, "%s: Y cent->x %d, cent->y %d, reversex %d," - "reversey %d motory %d data %d command %d", __FUNCTION__, - cent->x, cent->y, cnt->track.motorx_reverse, - cnt->track.motory_reverse, cnt->track.motory, command); - } - } - - return cnt->track.move_wait; + unsigned short int command = 0; + unsigned short int data = 0; + + /* If device is not open yet , open and center */ + if (cnt->track.dev < 0) { + if (!servo_center(cnt, 0, 0)) { + motion_log(LOG_INFO, 0, "%s: Problem opening servo!", __FUNCTION__); + return 0; + } + } + + if (debug_level >= TRACK_DEBUG) + motion_log(LOG_DEBUG, 0, "%s: cent->x %d, cent->y %d, reversex %d," + "reversey %d manual %d", __FUNCTION__, cent->x , cent->y, + cnt->track.motorx_reverse, cnt->track.motory_reverse, manual); + + + if (manual) { + int offset; + int position; + + if (cent->x) { + position = servo_position(cnt, cnt->track.motorx); + offset = cent->x * cnt->track.stepsize; + + + if ((cnt->track.motorx_reverse && (offset > 0)) || + (!cnt->track.motorx_reverse && (offset < 0))) + command = SERVO_COMMAND_LEFT_N; + else + command = SERVO_COMMAND_RIGHT_N; + + data = abs(offset); + + if ((data + position > cnt->track.maxx) || (position - offset < cnt->track.minx)) { + motion_log(LOG_INFO, 0, "%s: x %d value out of range! (%d - %d)", + __FUNCTION__, data, cnt->track.minx, cnt->track.maxx); + return 0; + } + + + /* Set Speed , TODO : it should be done only when speed changes */ + servo_command(cnt, cnt->track.motorx, SERVO_COMMAND_SPEED, cnt->track.speed); + servo_command(cnt, cnt->track.motorx, command, data); + } + + + if (cent->y) { + position = servo_position(cnt, cnt->track.motory); + offset = cent->y * cnt->track.stepsize; + + if ((cnt->track.motory_reverse && (offset > 0)) || + (!cnt->track.motory_reverse && (offset < 0))) + command = SERVO_COMMAND_UP_N; + else + command = SERVO_COMMAND_DOWN_N; + + data = abs(offset); + + if ((data + position > cnt->track.maxy) || (position - offset < cnt->track.miny)) { + motion_log(LOG_INFO, 0, "%s: y %d value out of range! (%d - %d)", + __FUNCTION__, data, cnt->track.miny, cnt->track.maxy); + return 0; + } + + + /* Set Speed , TODO : it should be done only when speed changes */ + servo_command(cnt, cnt->track.motory, SERVO_COMMAND_SPEED, cnt->track.speed); + servo_command(cnt, cnt->track.motory, command, data); + } + + } else { + int position; + + /***** x-axis *****/ + + /* Move left */ + if (cent->x < imgs->width / 2) { + if (cnt->track.motorx_reverse) + command = SERVO_COMMAND_RIGHT_N; + else + command = SERVO_COMMAND_LEFT_N; + data = imgs->width / 2 - cent->x; + } + + /* Move right */ + if (cent->x > imgs->width / 2) { + if (cnt->track.motorx_reverse) + command = SERVO_COMMAND_LEFT_N; + else + command = SERVO_COMMAND_RIGHT_N; + data = cent->x - imgs->width / 2; + } + + + if (debug_level >= TRACK_DEBUG) + motion_log(LOG_DEBUG, 0, "%s: X offset %d", __FUNCTION__, data); + + data = data * cnt->track.stepsize / imgs->width; + + if (data && command) { + + // TODO: need to get position to avoid overflow limits + position = servo_position(cnt, cnt->track.motorx); + + if ((position + data > cnt->track.maxx) || (position - data < cnt->track.minx)) { + motion_log(LOG_INFO, 0, "%s: x %d value out of range! (%d - %d)", + __FUNCTION__, data, cnt->track.minx, cnt->track.maxx); + return 0; + } + + /* Set Speed , TODO : it should be done only when speed changes */ + + servo_command(cnt, cnt->track.motorx, SERVO_COMMAND_SPEED, cnt->track.speed); + servo_command(cnt, cnt->track.motorx, command, data); + + if (debug_level >= TRACK_DEBUG) + motion_log(LOG_DEBUG, 0, "%s: X cent->x %d, cent->y %d, reversex %d," + "reversey %d motorx %d data %d command %d", __FUNCTION__, + cent->x, cent->y, cnt->track.motorx_reverse, + cnt->track.motory_reverse, cnt->track.motorx, data, command); + } + + /***** y-axis *****/ + + /* Move down */ + if (cent->y < imgs->height / 2) { + if (cnt->track.motory_reverse) + command = SERVO_COMMAND_UP_N; + else + command = SERVO_COMMAND_DOWN_N; + data = imgs->height / 2 - cent->y; + } + + /* Move up */ + if (cent->y > imgs->height / 2) { + if (cnt->track.motory_reverse) + command = SERVO_COMMAND_DOWN_N; + else + command = SERVO_COMMAND_UP_N; + data = cent->y - imgs->height / 2; + } + + if (debug_level >= TRACK_DEBUG) + motion_log(LOG_DEBUG, 0, "%s: Y offset %d", __FUNCTION__, data); + + data = data * cnt->track.stepsize / imgs->height; + + if (data && command) { + + // TODO: need to get position to avoid overflow limits + position = servo_position(cnt, cnt->track.motory); + + if ((position + data > cnt->track.maxy) || (position - data < cnt->track.miny)) { + motion_log(LOG_INFO, 0, "%s: y %d value out of range! (%d - %d)", + __FUNCTION__, data, cnt->track.miny, cnt->track.maxy); + return 0; + } + + /* Set Speed , TODO : it should be done only when speed changes */ + servo_command(cnt, cnt->track.motory, SERVO_COMMAND_SPEED, cnt->track.speed); + servo_command(cnt, cnt->track.motory, command, data); + + if (debug_level >= TRACK_DEBUG) + motion_log(LOG_DEBUG, 0, "%s: Y cent->x %d, cent->y %d, reversex %d," + "reversey %d motory %d data %d command %d", __FUNCTION__, + cent->x, cent->y, cnt->track.motorx_reverse, + cnt->track.motory_reverse, cnt->track.motory, command); + } + } + + return cnt->track.move_wait; } static unsigned short int servo_status(struct context *cnt, unsigned short int motor) { - return servo_command(cnt, motor, SERVO_COMMAND_STATUS, 0); + return servo_command(cnt, motor, SERVO_COMMAND_STATUS, 0); } @@ -572,685 +576,687 @@ static unsigned short int servo_status(struct context *cnt, unsigned short int static unsigned short int servo_center(struct context *cnt, int x_offset, int y_offset) { - unsigned short int ret = 0; - int x_offset_abs; - int y_offset_abs; - - /* If device is not open yet */ - if (cnt->track.dev < 0) { - if (!servo_open(cnt)) { - motion_log(LOG_ERR, 0, "%s: Problem opening servo!", __FUNCTION__); - return 0; - } - } - - - if (debug_level >= TRACK_DEBUG) - motion_log(LOG_DEBUG, 0, "%s: X-offset %d, Y-offset %d, x-position %d. y-position %d," - "reversex %d, reversey %d , stepsize %d", __FUNCTION__, x_offset, y_offset, - cnt->track.homex + (x_offset * cnt->track.stepsize), - cnt->track.homey + (y_offset * cnt->track.stepsize), - cnt->track.motorx_reverse, cnt->track.motory_reverse, - cnt->track.stepsize); - - /* x-axis */ - if (cnt->track.motorx_reverse) - x_offset_abs = (128 - cnt->track.homex) - (x_offset * cnt->track.stepsize) + 128; - else - x_offset_abs = cnt->track.homex + (x_offset * cnt->track.stepsize); - - if ( x_offset_abs <= cnt->track.maxx && x_offset_abs >= cnt->track.minx ) { - /* Set Speed , TODO : it should be done only when speed changes */ - servo_command(cnt, cnt->track.motorx, SERVO_COMMAND_SPEED, cnt->track.speed); - ret = servo_command(cnt, cnt->track.motorx, SERVO_COMMAND_ABSOLUTE, x_offset_abs); - } - - /* y-axis */ - if (cnt->track.motory_reverse) - y_offset_abs = (128 - cnt->track.homey) - (y_offset * cnt->track.stepsize) + 128; - else - y_offset_abs = cnt->track.homey + (y_offset * cnt->track.stepsize); - - if ( y_offset_abs <= cnt->track.maxy && y_offset_abs >= cnt->track.minx ) { - /* Set Speed , TODO : it should be done only when speed changes */ - servo_command(cnt, cnt->track.motory, SERVO_COMMAND_SPEED, cnt->track.speed); - ret = servo_command(cnt, cnt->track.motory, SERVO_COMMAND_ABSOLUTE, y_offset_abs); - } - - - return cnt->track.move_wait; + unsigned short int ret = 0; + int x_offset_abs; + int y_offset_abs; + + /* If device is not open yet */ + if (cnt->track.dev < 0) { + if (!servo_open(cnt)) { + motion_log(LOG_ERR, 0, "%s: Problem opening servo!", __FUNCTION__); + return 0; + } + } + + + if (debug_level >= TRACK_DEBUG) + motion_log(LOG_DEBUG, 0, "%s: X-offset %d, Y-offset %d, x-position %d. y-position %d," + "reversex %d, reversey %d , stepsize %d", __FUNCTION__, x_offset, y_offset, + cnt->track.homex + (x_offset * cnt->track.stepsize), + cnt->track.homey + (y_offset * cnt->track.stepsize), + cnt->track.motorx_reverse, cnt->track.motory_reverse, + cnt->track.stepsize); + + /* x-axis */ + if (cnt->track.motorx_reverse) + x_offset_abs = (128 - cnt->track.homex) - (x_offset * cnt->track.stepsize) + 128; + else + x_offset_abs = cnt->track.homex + (x_offset * cnt->track.stepsize); + + if (x_offset_abs <= cnt->track.maxx && x_offset_abs >= cnt->track.minx) { + /* Set Speed , TODO : it should be done only when speed changes */ + servo_command(cnt, cnt->track.motorx, SERVO_COMMAND_SPEED, cnt->track.speed); + ret = servo_command(cnt, cnt->track.motorx, SERVO_COMMAND_ABSOLUTE, x_offset_abs); + } + + /* y-axis */ + if (cnt->track.motory_reverse) + y_offset_abs = (128 - cnt->track.homey) - (y_offset * cnt->track.stepsize) + 128; + else + y_offset_abs = cnt->track.homey + (y_offset * cnt->track.stepsize); + + if (y_offset_abs <= cnt->track.maxy && y_offset_abs >= cnt->track.minx) { + /* Set Speed , TODO : it should be done only when speed changes */ + servo_command(cnt, cnt->track.motory, SERVO_COMMAND_SPEED, cnt->track.speed); + ret = servo_command(cnt, cnt->track.motory, SERVO_COMMAND_ABSOLUTE, y_offset_abs); + } + + + return cnt->track.move_wait; } /****************************************************************************** - Iomojo Smilecam on serial port + Iomojo Smilecam on serial port ******************************************************************************/ static char iomojo_command(struct context *cnt, char *command, unsigned short int len, unsigned short int ret) { - char buffer[1]; - time_t timeout = time(NULL); - - if (write(cnt->track.dev, command, len) != len) - return 0; - - if (ret) { - while (read(cnt->track.dev, buffer, 1) != 1 && time(NULL) < timeout + 2); - - if (time(NULL) >= timeout + 2) { - motion_log(LOG_ERR, 1, "%s: Return byte timeout!", __FUNCTION__); - return 0; - } - } - /* range values ? */ - return buffer[0]; + char buffer[1]; + time_t timeout = time(NULL); + + if (write(cnt->track.dev, command, len) != len) + return 0; + + if (ret) { + while (read(cnt->track.dev, buffer, 1) != 1 && time(NULL) < timeout + 2); + + if (time(NULL) >= timeout + 2) { + motion_log(LOG_ERR, 1, "%s: Return byte timeout!", __FUNCTION__); + return 0; + } + } + /* range values ? */ + return buffer[0]; } static void iomojo_setspeed(struct context *cnt, unsigned short int speed) { - char command[3]; - - command[0] = IOMOJO_SETSPEED_CMD; - command[1] = cnt->track.iomojo_id; - command[2] = speed; - - if (iomojo_command(cnt, command, 3, 1) != IOMOJO_SETSPEED_RET) - motion_log(LOG_ERR, 1, "%s: Unable to set camera speed", __FUNCTION__); + char command[3]; + + command[0] = IOMOJO_SETSPEED_CMD; + command[1] = cnt->track.iomojo_id; + command[2] = speed; + + if (iomojo_command(cnt, command, 3, 1) != IOMOJO_SETSPEED_RET) + motion_log(LOG_ERR, 1, "%s: Unable to set camera speed", __FUNCTION__); } static void iomojo_movehome(struct context *cnt) { - char command[2]; - - command[0] = IOMOJO_MOVEHOME; - command[1] = cnt->track.iomojo_id; + char command[2]; + + command[0] = IOMOJO_MOVEHOME; + command[1] = cnt->track.iomojo_id; - iomojo_command(cnt, command, 2, 0); + iomojo_command(cnt, command, 2, 0); } static unsigned short int iomojo_center(struct context *cnt, int x_offset, int y_offset) { - struct termios adtio; - char command[5], direction = 0; - - if (cnt->track.dev<0) { - if ((cnt->track.dev = open(cnt->track.port, O_RDWR | O_NOCTTY)) < 0) { - motion_log(LOG_ERR, 1, "%s: Unable to open serial device %s", - __FUNCTION__, cnt->track.port); - return 0; - } - bzero (&adtio, sizeof(adtio)); - adtio.c_cflag = IOMOJO_BAUDRATE | CS8 | CLOCAL | CREAD; - adtio.c_iflag = IGNPAR; - adtio.c_oflag = 0; - adtio.c_lflag = 0; /* non-canon, no echo */ - adtio.c_cc[VTIME] = 0; /* timer unused */ - adtio.c_cc[VMIN] = 0; /* blocking read until 1 char */ - tcflush(cnt->track.dev, TCIFLUSH); - if (tcsetattr(cnt->track.dev, TCSANOW, &adtio) < 0) { - motion_log(LOG_ERR, 1, "%s: Unable to initialize serial device %s", - __FUNCTION__, cnt->track.port); - return 0; - } - } - - iomojo_setspeed(cnt, 40); - iomojo_movehome(cnt); - - if (x_offset || y_offset) { - if (x_offset > 0) - direction |= IOMOJO_DIRECTION_RIGHT; - else { - direction |= IOMOJO_DIRECTION_LEFT; - x_offset *= -1; - } - - if (y_offset > 0) - direction |= IOMOJO_DIRECTION_UP; - else { - direction |= IOMOJO_DIRECTION_DOWN; - y_offset *= -1; - } - - if (x_offset > 180) - x_offset = 180; - - if (y_offset > 60) - y_offset = 60; - - command[0] = IOMOJO_MOVEOFFSET_CMD; - command[1] = cnt->track.iomojo_id; - command[2] = direction; - command[3] = x_offset; - command[4] = y_offset; - iomojo_command(cnt, command, 5, 0); - } - - motion_log(LOG_INFO, 0, "%s: succeed", __FUNCTION__); - - return cnt->track.move_wait; + struct termios adtio; + char command[5], direction = 0; + + if (cnt->track.dev < 0) { + if ((cnt->track.dev = open(cnt->track.port, O_RDWR | O_NOCTTY)) < 0) { + motion_log(LOG_ERR, 1, "%s: Unable to open serial device %s", + __FUNCTION__, cnt->track.port); + return 0; + } + + bzero (&adtio, sizeof(adtio)); + adtio.c_cflag = IOMOJO_BAUDRATE | CS8 | CLOCAL | CREAD; + adtio.c_iflag = IGNPAR; + adtio.c_oflag = 0; + adtio.c_lflag = 0; /* non-canon, no echo */ + adtio.c_cc[VTIME] = 0; /* timer unused */ + adtio.c_cc[VMIN] = 0; /* blocking read until 1 char */ + tcflush(cnt->track.dev, TCIFLUSH); + if (tcsetattr(cnt->track.dev, TCSANOW, &adtio) < 0) { + motion_log(LOG_ERR, 1, "%s: Unable to initialize serial device %s", + __FUNCTION__, cnt->track.port); + return 0; + } + } + + iomojo_setspeed(cnt, 40); + iomojo_movehome(cnt); + + if (x_offset || y_offset) { + if (x_offset > 0) { + direction |= IOMOJO_DIRECTION_RIGHT; + } else { + direction |= IOMOJO_DIRECTION_LEFT; + x_offset *= -1; + } + + if (y_offset > 0) { + direction |= IOMOJO_DIRECTION_UP; + } else { + direction |= IOMOJO_DIRECTION_DOWN; + y_offset *= -1; + } + + if (x_offset > 180) + x_offset = 180; + + if (y_offset > 60) + y_offset = 60; + + command[0] = IOMOJO_MOVEOFFSET_CMD; + command[1] = cnt->track.iomojo_id; + command[2] = direction; + command[3] = x_offset; + command[4] = y_offset; + iomojo_command(cnt, command, 5, 0); + } + + motion_log(LOG_INFO, 0, "%s: succeed", __FUNCTION__); + + return cnt->track.move_wait; } static unsigned short int iomojo_move(struct context *cnt, int dev, struct coord *cent, struct images *imgs) { - char command[5]; - int direction = 0; - int nx = 0, ny = 0; - int i; - - if (dev < 0) - if (!iomojo_center(cnt, 0, 0)) - return 0; - - if (cent->x < imgs->width / 2) { - direction |= IOMOJO_DIRECTION_LEFT; - nx = imgs->width / 2 - cent->x; - } - - if (cent->x > imgs->width / 2) { - direction |= IOMOJO_DIRECTION_RIGHT; - nx = cent->x - imgs->width / 2; - } - - if (cent->y < imgs->height / 2) { - direction |= IOMOJO_DIRECTION_DOWN; - ny = imgs->height / 2 - cent->y; - } - - if (cent->y > imgs->height / 2) { - direction |= IOMOJO_DIRECTION_UP; - ny = cent->y - imgs->height / 2; - } - - nx = nx * 72 / imgs->width; - ny = ny * 72 / imgs->height; - - if (nx || ny) { - if (nx > 180) - nx = 180; - - if (ny > 60) - ny = 60; - - command[0] = IOMOJO_MOVEOFFSET_CMD; - command[1] = cnt->track.iomojo_id; - command[2] = direction; - command[3] = nx; - command[4] = ny; - iomojo_command(cnt, command, 5, 0); - - /* Number of frames to skip while moving */ - if (ny >= nx) - i = 25 * ny / 90; - else - i = 25 * nx / 90; - return i; - } - - return 0; + char command[5]; + int direction = 0; + int nx = 0, ny = 0; + int i; + + if (dev < 0) + if (!iomojo_center(cnt, 0, 0)) + return 0; + + if (cent->x < imgs->width / 2) { + direction |= IOMOJO_DIRECTION_LEFT; + nx = imgs->width / 2 - cent->x; + } + + if (cent->x > imgs->width / 2) { + direction |= IOMOJO_DIRECTION_RIGHT; + nx = cent->x - imgs->width / 2; + } + + if (cent->y < imgs->height / 2) { + direction |= IOMOJO_DIRECTION_DOWN; + ny = imgs->height / 2 - cent->y; + } + + if (cent->y > imgs->height / 2) { + direction |= IOMOJO_DIRECTION_UP; + ny = cent->y - imgs->height / 2; + } + + nx = nx * 72 / imgs->width; + ny = ny * 72 / imgs->height; + + if (nx || ny) { + if (nx > 180) + nx = 180; + + if (ny > 60) + ny = 60; + + command[0] = IOMOJO_MOVEOFFSET_CMD; + command[1] = cnt->track.iomojo_id; + command[2] = direction; + command[3] = nx; + command[4] = ny; + iomojo_command(cnt, command, 5, 0); + + /* Number of frames to skip while moving */ + if (ny >= nx) + i = 25 * ny / 90; + else + i = 25 * nx / 90; + return i; + } + + return 0; } /****************************************************************************** - Logitech QuickCam Orbit camera tracking code by folkert@vanheusden.com + Logitech QuickCam Orbit camera tracking code by folkert@vanheusden.com ******************************************************************************/ #ifndef WITHOUT_V4L static unsigned short int lqos_center(struct context *cnt, int dev, int x_angle, int y_angle) { - int reset = 3; - struct pwc_mpt_angles pma; - struct pwc_mpt_range pmr; - - if (cnt->track.dev == -1) { - - if (ioctl(dev, VIDIOCPWCMPTRESET, &reset) == -1) { - motion_log(LOG_ERR, 1, "%s: Failed to reset pwc camera to starting position! Reason", - __FUNCTION__); - return 0; - } - - SLEEP(6, 0); - - if (ioctl(dev, VIDIOCPWCMPTGRANGE, &pmr) == -1) { - motion_log(LOG_ERR, 1, "%s: failed VIDIOCPWCMPTGRANGE", __FUNCTION__); - return 0; - } - - cnt->track.dev = dev; - cnt->track.minmaxfound = 1; - cnt->track.minx = pmr.pan_min; - cnt->track.maxx = pmr.pan_max; - cnt->track.miny = pmr.tilt_min; - cnt->track.maxy = pmr.tilt_max; - } - - if (ioctl(dev, VIDIOCPWCMPTGANGLE, &pma) == -1) - motion_log(LOG_ERR, 1, "%s: ioctl VIDIOCPWCMPTGANGLE", __FUNCTION__); - - pma.absolute = 1; + int reset = 3; + struct pwc_mpt_angles pma; + struct pwc_mpt_range pmr; + + if (cnt->track.dev == -1) { + + if (ioctl(dev, VIDIOCPWCMPTRESET, &reset) == -1) { + motion_log(LOG_ERR, 1, "%s: Failed to reset pwc camera to starting position! Reason", + __FUNCTION__); + return 0; + } + + SLEEP(6, 0); + + if (ioctl(dev, VIDIOCPWCMPTGRANGE, &pmr) == -1) { + motion_log(LOG_ERR, 1, "%s: failed VIDIOCPWCMPTGRANGE", __FUNCTION__); + return 0; + } + + cnt->track.dev = dev; + cnt->track.minmaxfound = 1; + cnt->track.minx = pmr.pan_min; + cnt->track.maxx = pmr.pan_max; + cnt->track.miny = pmr.tilt_min; + cnt->track.maxy = pmr.tilt_max; + } + + if (ioctl(dev, VIDIOCPWCMPTGANGLE, &pma) == -1) + motion_log(LOG_ERR, 1, "%s: ioctl VIDIOCPWCMPTGANGLE", __FUNCTION__); + + pma.absolute = 1; - if (x_angle * 100 < cnt->track.maxx && x_angle * 100 > cnt->track.minx) - pma.pan = x_angle * 100; + if (x_angle * 100 < cnt->track.maxx && x_angle * 100 > cnt->track.minx) + pma.pan = x_angle * 100; - if (y_angle * 100 < cnt->track.maxy && y_angle * 100 > cnt->track.miny) - pma.tilt = y_angle * 100; + if (y_angle * 100 < cnt->track.maxy && y_angle * 100 > cnt->track.miny) + pma.tilt = y_angle * 100; - if (ioctl(dev, VIDIOCPWCMPTSANGLE, &pma) == -1) { - motion_log(LOG_ERR, 1, "%s: Failed to pan/tilt pwc camera! Reason", __FUNCTION__); - return 0; - } + if (ioctl(dev, VIDIOCPWCMPTSANGLE, &pma) == -1) { + motion_log(LOG_ERR, 1, "%s: Failed to pan/tilt pwc camera! Reason", __FUNCTION__); + return 0; + } - motion_log(LOG_INFO, 0, "%s: succeed", __FUNCTION__); + motion_log(LOG_INFO, 0, "%s: succeed", __FUNCTION__); - return cnt->track.move_wait; + return cnt->track.move_wait; } static unsigned short int lqos_move(struct context *cnt, int dev, struct coord *cent, struct images *imgs, unsigned short int manual) { - int delta_x = cent->x - (imgs->width / 2); - int delta_y = cent->y - (imgs->height / 2); - int move_x_degrees, move_y_degrees; - struct pwc_mpt_angles pma; - struct pwc_mpt_range pmr; - - /* If we are on auto track we calculate delta, otherwise we use user input in degrees times 100 */ - if (!manual) { - if (delta_x > imgs->width * 3/8 && delta_x < imgs->width * 5/8) - return 0; - if (delta_y > imgs->height * 3/8 && delta_y < imgs->height * 5/8) - return 0; - - move_x_degrees = delta_x * cnt->track.step_angle_x * 100 / (imgs->width / 2); - move_y_degrees = -delta_y * cnt->track.step_angle_y * 100 / (imgs->height / 2); - } else { - move_x_degrees = cent->x * 100; - move_y_degrees = cent->y * 100; - } - - /* If we never checked for the min/max values for pan/tilt we do it now */ - if (cnt->track.minmaxfound == 0) { - if (ioctl(dev, VIDIOCPWCMPTGRANGE, &pmr) == -1) { - motion_log(LOG_ERR, 1, "%s: failed VIDIOCPWCMPTGRANGE", __FUNCTION__); - return 0; - } - cnt->track.minmaxfound = 1; - cnt->track.minx = pmr.pan_min; - cnt->track.maxx = pmr.pan_max; - cnt->track.miny = pmr.tilt_min; - cnt->track.maxy = pmr.tilt_max; - } - - /* Get current camera position */ - if (ioctl(dev, VIDIOCPWCMPTGANGLE, &pma) == -1) - motion_log(LOG_ERR, 1, "%s: ioctl VIDIOCPWCMPTGANGLE", __FUNCTION__); - - - /* Check current position of camera and see if we need to adjust - values down to what is left to move */ - if (move_x_degrees<0 && (cnt->track.minx - pma.pan) > move_x_degrees) - move_x_degrees = (cnt->track.minx - pma.pan); - - if (move_x_degrees>0 && (cnt->track.maxx - pma.pan) < move_x_degrees) - move_x_degrees = (cnt->track.maxx - pma.pan); - - if (move_y_degrees<0 && (cnt->track.miny - pma.tilt) > move_y_degrees) - move_y_degrees = (cnt->track.miny - pma.tilt); - - if (move_y_degrees>0 && (cnt->track.maxy - pma.tilt) < move_y_degrees) - move_y_degrees = (cnt->track.maxy - pma.tilt); - - /* Move camera relative to current position */ - pma.absolute = 0; - pma.pan = move_x_degrees; - pma.tilt = move_y_degrees; - - if (ioctl(dev, VIDIOCPWCMPTSANGLE, &pma) == -1) { - motion_log(LOG_ERR, 1, "%s: Failed to pan/tilt pwc camera! Reason", __FUNCTION__); - return 0; - } - - return cnt->track.move_wait; + int delta_x = cent->x - (imgs->width / 2); + int delta_y = cent->y - (imgs->height / 2); + int move_x_degrees, move_y_degrees; + struct pwc_mpt_angles pma; + struct pwc_mpt_range pmr; + + /* If we are on auto track we calculate delta, otherwise we use user input in degrees times 100 */ + if (!manual) { + if (delta_x > imgs->width * 3/8 && delta_x < imgs->width * 5/8) + return 0; + if (delta_y > imgs->height * 3/8 && delta_y < imgs->height * 5/8) + return 0; + + move_x_degrees = delta_x * cnt->track.step_angle_x * 100 / (imgs->width / 2); + move_y_degrees = -delta_y * cnt->track.step_angle_y * 100 / (imgs->height / 2); + } else { + move_x_degrees = cent->x * 100; + move_y_degrees = cent->y * 100; + } + + /* If we never checked for the min/max values for pan/tilt we do it now */ + if (cnt->track.minmaxfound == 0) { + if (ioctl(dev, VIDIOCPWCMPTGRANGE, &pmr) == -1) { + motion_log(LOG_ERR, 1, "%s: failed VIDIOCPWCMPTGRANGE", __FUNCTION__); + return 0; + } + cnt->track.minmaxfound = 1; + cnt->track.minx = pmr.pan_min; + cnt->track.maxx = pmr.pan_max; + cnt->track.miny = pmr.tilt_min; + cnt->track.maxy = pmr.tilt_max; + } + + /* Get current camera position */ + if (ioctl(dev, VIDIOCPWCMPTGANGLE, &pma) == -1) + motion_log(LOG_ERR, 1, "%s: ioctl VIDIOCPWCMPTGANGLE", __FUNCTION__); + + + /* Check current position of camera and see if we need to adjust + values down to what is left to move */ + if (move_x_degrees<0 && (cnt->track.minx - pma.pan) > move_x_degrees) + move_x_degrees = (cnt->track.minx - pma.pan); + + if (move_x_degrees>0 && (cnt->track.maxx - pma.pan) < move_x_degrees) + move_x_degrees = (cnt->track.maxx - pma.pan); + + if (move_y_degrees<0 && (cnt->track.miny - pma.tilt) > move_y_degrees) + move_y_degrees = (cnt->track.miny - pma.tilt); + + if (move_y_degrees>0 && (cnt->track.maxy - pma.tilt) < move_y_degrees) + move_y_degrees = (cnt->track.maxy - pma.tilt); + + /* Move camera relative to current position */ + pma.absolute = 0; + pma.pan = move_x_degrees; + pma.tilt = move_y_degrees; + + if (ioctl(dev, VIDIOCPWCMPTSANGLE, &pma) == -1) { + motion_log(LOG_ERR, 1, "%s: Failed to pan/tilt pwc camera! Reason", __FUNCTION__); + return 0; + } + + return cnt->track.move_wait; } /****************************************************************************** - Logitech QuickCam Sphere camera tracking code by oBi - - Modify by Dirk Wesenberg(Munich) 30.03.07 - - for new API in uvcvideo - - add Trace-steps for investigation + Logitech QuickCam Sphere camera tracking code by oBi + + Modify by Dirk Wesenberg(Munich) 30.03.07 + - for new API in uvcvideo + - add Trace-steps for investigation ******************************************************************************/ #ifdef MOTION_V4L2 static unsigned short int uvc_center(struct context *cnt, int dev, int x_angle, int y_angle) { - /* CALC ABSOLUTE MOVING : Act.Position +/- delta to request X and Y */ - int move_x_degrees = 0, move_y_degrees = 0; - - union pantilt { - struct { - short pan; - short tilt; - } s16; - int value; - }; - union pantilt pan; - - if (cnt->track.dev == -1) { - - int reset = 3; //0-non reset, 1-reset pan, 2-reset tilt, 3-reset pan&tilt - struct v4l2_control control_s; - - control_s.id = V4L2_CID_PANTILT_RESET; - control_s.value = (unsigned char) reset; - - if (ioctl(dev, VIDIOC_S_CTRL, &control_s) < 0) { - motion_log(LOG_ERR, 1, "%s: Failed to reset UVC camera to starting position! Reason", - __FUNCTION__); - return 0; - } - - if (debug_level >= TRACK_DEBUG) - motion_log(LOG_DEBUG, 0, "%s: Reseting UVC camera to starting position", - __FUNCTION__); - - SLEEP(8, 0); - - /* Get camera range */ - struct v4l2_queryctrl queryctrl; - - queryctrl.id = V4L2_CID_PAN_RELATIVE; - if (ioctl(dev, VIDIOC_QUERYCTRL, &queryctrl) < 0) { - motion_log(LOG_ERR, 1, "%s: ioctl querycontrol error %d", __FUNCTION__, errno); - return 0; - } - - if (debug_level >= TRACK_DEBUG) - motion_log(LOG_DEBUG, 0, "%s: Getting camera range", __FUNCTION__); - - - /* DWe 30.03.07 The orig request failed : - * must be VIDIOC_G_CTRL separate for pan and tilt or via VIDIOC_G_EXT_CTRLS - now for 1st manual - * Range X = -70 to +70 degrees - * Y = -30 to +30 degrees - */ - - //get mininum -// pan.value = queryctrl.minimum; - - cnt->track.minx = -4480 / INCPANTILT; - cnt->track.miny = -1920 / INCPANTILT; - //get maximum - cnt->track.maxx = 4480 / INCPANTILT; - cnt->track.maxy = 1920 / INCPANTILT; -// pan.value = queryctrl.maximum; - - cnt->track.dev = dev; - cnt->track.pan_angle = 0; - cnt->track.tilt_angle = 0; - cnt->track.minmaxfound = 1; - - } - - struct v4l2_control control_s; - - if (debug_level >= TRACK_DEBUG) { - motion_log(LOG_DEBUG, 0, "%s: INPUT_PARAM_ABS pan_min %d,pan_max %d,tilt_min %d,tilt_max %d ", - __FUNCTION__, cnt->track.minx, cnt->track.maxx, cnt->track.miny, cnt->track.maxy ); - motion_log(LOG_DEBUG, 0, "%s: INPUT_PARAM_ABS X_Angel %d, Y_Angel %d ", - __FUNCTION__, x_angle, y_angle); - } - - if (x_angle <= cnt->track.maxx && x_angle >= cnt->track.minx) - move_x_degrees = x_angle - (cnt->track.pan_angle); - - if (y_angle <= cnt->track.maxy && y_angle >= cnt->track.miny) - move_y_degrees = y_angle - (cnt->track.tilt_angle); - - - /* - tilt up: - value - tilt down: + value - pan left: - value - pan right: + value - */ - pan.s16.pan = -move_x_degrees * INCPANTILT; - pan.s16.tilt = -move_y_degrees * INCPANTILT; - - if (debug_level >= TRACK_DEBUG) - motion_log(LOG_DEBUG, 0, "%s: For_SET_ABS move_X %d,move_Y %d", - __FUNCTION__, move_x_degrees, move_y_degrees); - - /* DWe 30.03.07 Must be broken in diff calls, because - - one call for both is not accept via VIDIOC_S_CTRL -> maybe via VIDIOC_S_EXT_CTRLS - - The Webcam or uvcvideo does not like a call with a zero-move - */ + /* CALC ABSOLUTE MOVING : Act.Position +/- delta to request X and Y */ + int move_x_degrees = 0, move_y_degrees = 0; + + union pantilt { + struct { + short pan; + short tilt; + } s16; + int value; + }; + union pantilt pan; + + if (cnt->track.dev == -1) { + + int reset = 3; //0-non reset, 1-reset pan, 2-reset tilt, 3-reset pan&tilt + struct v4l2_control control_s; + + control_s.id = V4L2_CID_PANTILT_RESET; + control_s.value = (unsigned char) reset; + + if (ioctl(dev, VIDIOC_S_CTRL, &control_s) < 0) { + motion_log(LOG_ERR, 1, "%s: Failed to reset UVC camera to starting position! Reason", + __FUNCTION__); + return 0; + } + + if (debug_level >= TRACK_DEBUG) + motion_log(LOG_DEBUG, 0, "%s: Reseting UVC camera to starting position", + __FUNCTION__); + + SLEEP(8, 0); + + /* Get camera range */ + struct v4l2_queryctrl queryctrl; + + queryctrl.id = V4L2_CID_PAN_RELATIVE; + if (ioctl(dev, VIDIOC_QUERYCTRL, &queryctrl) < 0) { + motion_log(LOG_ERR, 1, "%s: ioctl querycontrol error %d", __FUNCTION__, errno); + return 0; + } + + if (debug_level >= TRACK_DEBUG) + motion_log(LOG_DEBUG, 0, "%s: Getting camera range", __FUNCTION__); + + + /* DWe 30.03.07 The orig request failed : + * must be VIDIOC_G_CTRL separate for pan and tilt or via VIDIOC_G_EXT_CTRLS - now for 1st manual + * Range X = -70 to +70 degrees + * Y = -30 to +30 degrees + */ + + //get mininum + //pan.value = queryctrl.minimum; + + cnt->track.minx = -4480 / INCPANTILT; + cnt->track.miny = -1920 / INCPANTILT; + //get maximum + cnt->track.maxx = 4480 / INCPANTILT; + cnt->track.maxy = 1920 / INCPANTILT; + //pan.value = queryctrl.maximum; + + cnt->track.dev = dev; + cnt->track.pan_angle = 0; + cnt->track.tilt_angle = 0; + cnt->track.minmaxfound = 1; + + } + + struct v4l2_control control_s; + + if (debug_level >= TRACK_DEBUG) { + motion_log(LOG_DEBUG, 0, "%s: INPUT_PARAM_ABS pan_min %d,pan_max %d,tilt_min %d,tilt_max %d ", + __FUNCTION__, cnt->track.minx, cnt->track.maxx, cnt->track.miny, cnt->track.maxy); + motion_log(LOG_DEBUG, 0, "%s: INPUT_PARAM_ABS X_Angel %d, Y_Angel %d ", + __FUNCTION__, x_angle, y_angle); + } + + if (x_angle <= cnt->track.maxx && x_angle >= cnt->track.minx) + move_x_degrees = x_angle - (cnt->track.pan_angle); + + if (y_angle <= cnt->track.maxy && y_angle >= cnt->track.miny) + move_y_degrees = y_angle - (cnt->track.tilt_angle); + + + /* + tilt up: - value + tilt down: + value + pan left: - value + pan right: + value + */ + pan.s16.pan = -move_x_degrees * INCPANTILT; + pan.s16.tilt = -move_y_degrees * INCPANTILT; + + if (debug_level >= TRACK_DEBUG) + motion_log(LOG_DEBUG, 0, "%s: For_SET_ABS move_X %d,move_Y %d", + __FUNCTION__, move_x_degrees, move_y_degrees); + + /* DWe 30.03.07 Must be broken in diff calls, because + - one call for both is not accept via VIDIOC_S_CTRL -> maybe via VIDIOC_S_EXT_CTRLS + - The Webcam or uvcvideo does not like a call with a zero-move + */ - if (move_x_degrees != 0 ) { - control_s.id = V4L2_CID_PAN_RELATIVE; - // control_s.value = pan.value; - control_s.value = pan.s16.pan; - if (ioctl(dev, VIDIOC_S_CTRL, &control_s) < 0) { - motion_log(LOG_ERR, 1, "%s: Failed to move UVC camera!", __FUNCTION__); - return 0; - } - } - - /* DWe 30.03.07 We must wait a little,before we set the next CMD, otherwise PAN is mad ... */ - if ((move_x_degrees != 0) && (move_y_degrees != 0)) { - SLEEP (1, 0); - } - - if (move_y_degrees != 0) { - control_s.id = V4L2_CID_TILT_RELATIVE; - // control_s.value = pan.value; - control_s.value = pan.s16.tilt; - if (ioctl(dev, VIDIOC_S_CTRL, &control_s) < 0) { - motion_log(LOG_ERR, 1, "%s: Failed to move UVC camera!", __FUNCTION__); - return 0; - } - } - - if (debug_level >= TRACK_DEBUG) - motion_log(LOG_DEBUG, 0, "%s: Found MINMAX = %d", __FUNCTION__, cnt->track.minmaxfound); - - if (cnt->track.dev != -1) { - motion_log(LOG_DEBUG, 0, "%s: Before_ABS_Y_Angel : x= %d , Y= %d, ", - __FUNCTION__, cnt->track.pan_angle, cnt->track.tilt_angle ); - - if (move_x_degrees != -1) { - cnt->track.pan_angle += move_x_degrees; - } - - if (move_x_degrees != -1) { - cnt->track.tilt_angle += move_y_degrees; - } - - if (debug_level >= TRACK_DEBUG) - motion_log(LOG_DEBUG, 0, "%s: After_ABS_Y_Angel : x= %d , Y= %d", - __FUNCTION__, cnt->track.pan_angle, cnt->track.tilt_angle ); - } - - return cnt->track.move_wait; + if (move_x_degrees != 0) { + control_s.id = V4L2_CID_PAN_RELATIVE; + //control_s.value = pan.value; + control_s.value = pan.s16.pan; + + if (ioctl(dev, VIDIOC_S_CTRL, &control_s) < 0) { + motion_log(LOG_ERR, 1, "%s: Failed to move UVC camera!", __FUNCTION__); + return 0; + } + } + + /* DWe 30.03.07 We must wait a little,before we set the next CMD, otherwise PAN is mad ... */ + if ((move_x_degrees != 0) && (move_y_degrees != 0)) + SLEEP(1, 0); + + if (move_y_degrees != 0) { + control_s.id = V4L2_CID_TILT_RELATIVE; + //control_s.value = pan.value; + control_s.value = pan.s16.tilt; + + if (ioctl(dev, VIDIOC_S_CTRL, &control_s) < 0) { + motion_log(LOG_ERR, 1, "%s: Failed to move UVC camera!", __FUNCTION__); + return 0; + } + } + + if (debug_level >= TRACK_DEBUG) + motion_log(LOG_DEBUG, 0, "%s: Found MINMAX = %d", __FUNCTION__, cnt->track.minmaxfound); + + if (cnt->track.dev != -1) { + motion_log(LOG_DEBUG, 0, "%s: Before_ABS_Y_Angel : x= %d , Y= %d, ", + __FUNCTION__, cnt->track.pan_angle, cnt->track.tilt_angle); + + if (move_x_degrees != -1) { + cnt->track.pan_angle += move_x_degrees; + } + + if (move_x_degrees != -1) { + cnt->track.tilt_angle += move_y_degrees; + } + + if (debug_level >= TRACK_DEBUG) + motion_log(LOG_DEBUG, 0, "%s: After_ABS_Y_Angel : x= %d , Y= %d", + __FUNCTION__, cnt->track.pan_angle, cnt->track.tilt_angle); + } + + return cnt->track.move_wait; } static unsigned short int uvc_move(struct context *cnt, int dev, struct coord *cent, struct images *imgs, unsigned short int manual) { - /* RELATIVE MOVING : Act.Position +/- X and Y */ - - int delta_x = cent->x - (imgs->width / 2); - int delta_y = cent->y - (imgs->height / 2); - int move_x_degrees, move_y_degrees; - - /* DWe 30.03.07 Does the request of act.position from WebCam work ? luvcview shows at every position 180 :( */ - /* Now we init the Web by call Reset, so we can sure, that we are at x/y = 0,0 */ - /* Don't worry, if the WebCam make a sound - over End at PAN - hmmm, should it be normal ...? */ - /* PAN Value 7777 in relative will init also a want reset for CAM - it will be "0" after that */ - if (( cnt->track.minmaxfound != 1) || (cent->x == 7777 )) { - unsigned short int reset = 3; //0-non reset, 1-reset pan, 2-reset tilt, 3-reset pan&tilt - struct v4l2_control control_s; - - control_s.id = V4L2_CID_PANTILT_RESET; - control_s.value = (unsigned char) reset; - - if (ioctl(dev, VIDIOC_S_CTRL, &control_s) < 0) { - motion_log(LOG_ERR, 1, "%s: Failed to reset UVC camera to starting position! Reason", - __FUNCTION__); - return 0; - } - if (debug_level >= TRACK_DEBUG) - motion_log(LOG_DEBUG, 0, "%s: Reseting UVC camera to starting position", __FUNCTION__); - - /* set the "helpvalue" back to null because after reset CAM should be in x=0 and not 70 */ - cent->x = 0; - SLEEP(8, 0); - - /* DWe 30.03.07 The orig request failed : - * must be VIDIOC_G_CTRL separate for pan and tilt or via VIDIOC_G_EXT_CTRLS - now for 1st manual - * Range X = -70 to +70 degrees - * Y = -30 to +30 degrees - */ - - cnt->track.minx = -4480 / INCPANTILT; - cnt->track.miny = -1920 / INCPANTILT; - cnt->track.maxx = 4480 / INCPANTILT; - cnt->track.maxy = 1920 / INCPANTILT; - cnt->track.dev = dev; - cnt->track.pan_angle = 0; - cnt->track.tilt_angle = 0; - cnt->track.minmaxfound = 1; - } - - - /* If we are on auto track we calculate delta, otherwise we use user input in degrees */ - if (!manual) { - if (delta_x > imgs->width * 3/8 && delta_x < imgs->width * 5/8) - return 0; - if (delta_y > imgs->height * 3/8 && delta_y < imgs->height * 5/8) - return 0; - - move_x_degrees = delta_x * cnt->track.step_angle_x / (imgs->width / 2); - move_y_degrees = -delta_y * cnt->track.step_angle_y / (imgs->height / 2); - } else { - move_x_degrees = cent->x; - move_y_degrees = cent->y; - } - - union pantilt { - struct { - short pan; - short tilt; - } s16; - int value; - }; - - struct v4l2_control control_s; - union pantilt pan; - - if (cnt->track.minmaxfound == 1) { - /* - * Check current position of camera and see if we need to adjust - * values down to what is left to move - */ - if (move_x_degrees < 0 && (cnt->track.minx - cnt->track.pan_angle) > move_x_degrees) - move_x_degrees = cnt->track.minx - cnt->track.pan_angle; - - if (move_x_degrees > 0 && (cnt->track.maxx - cnt->track.pan_angle) < move_x_degrees) - move_x_degrees = cnt->track.maxx - cnt->track.pan_angle; - - if (move_y_degrees < 0 && (cnt->track.miny - cnt->track.tilt_angle) > move_y_degrees) - move_y_degrees = cnt->track.miny - cnt->track.tilt_angle; - - if (move_y_degrees > 0 && (cnt->track.maxy - cnt->track.tilt_angle) < move_y_degrees) - move_y_degrees = cnt->track.maxy - cnt->track.tilt_angle; - } - - if (debug_level >= TRACK_DEBUG) { - motion_log(LOG_DEBUG, 0, "For_SET_REL pan_min %d,pan_max %d,tilt_min %d,tilt_max %d", - cnt->track.minx, cnt->track.maxx, cnt->track.miny, cnt->track.maxy); - motion_log(LOG_DEBUG, 0, "For_SET_REL track_pan_Angel %d, track_tilt_Angel %d", - cnt->track.pan_angle, cnt->track.tilt_angle); - motion_log(LOG_DEBUG, 0, "For_SET_REL move_X %d,move_Y %d", move_x_degrees, move_y_degrees); - } - - /* - tilt up: - value - tilt down: + value - pan left: - value - pan right: + value - */ - - pan.s16.pan = -move_x_degrees * INCPANTILT; - pan.s16.tilt = -move_y_degrees * INCPANTILT; - - /* DWe 30.03.07 Must be broken in diff calls, because - - one call for both is not accept via VIDIOC_S_CTRL -> maybe via VIDIOC_S_EXT_CTRLS - - The Webcam or uvcvideo does not like a call with a zero-move - */ - - if (move_x_degrees != 0) { - - control_s.id = V4L2_CID_PAN_RELATIVE; - - control_s.value = pan.s16.pan; - if (debug_level >= TRACK_DEBUG) - motion_log(LOG_DEBUG, 0, " dev %d, addr= %d, control_S= %d, Wert= %d", - dev, VIDIOC_S_CTRL, &control_s, pan.s16.pan); - - if (ioctl(dev, VIDIOC_S_CTRL, &control_s) < 0) { - motion_log(LOG_ERR, 1, "%s: Failed to move UVC camera!", __FUNCTION__); - return 0; - } - } - - /* DWe 30.03.07 We must wait a little,before we set the next CMD, otherwise PAN is mad ... */ - if ((move_x_degrees != 0) && (move_y_degrees != 0)) { - SLEEP (1, 0); - } - - - if (move_y_degrees != 0) { - - control_s.id = V4L2_CID_TILT_RELATIVE; - - control_s.value = pan.s16.tilt; - if (debug_level >= TRACK_DEBUG) - motion_log(LOG_DEBUG, 0, " dev %d,addr= %d, control_S= %d, Wert= %d", - dev, VIDIOC_S_CTRL, &control_s, pan.s16.tilt); - - if (ioctl(dev, VIDIOC_S_CTRL, &control_s) < 0) { - motion_log(LOG_ERR, 1, "%s: Failed to move UVC camera!", __FUNCTION__); - return 0; - } - } - - if (debug_level >= TRACK_DEBUG) - motion_log(LOG_DEBUG, 0, "%s: Found MINMAX = %d", - __FUNCTION__, cnt->track.minmaxfound); - - if (cnt->track.minmaxfound == 1) { - if (debug_level >= TRACK_DEBUG) - motion_log(LOG_DEBUG, 0, "Before_REL_Y_Angel : x= %d , Y= %d", - cnt->track.pan_angle, cnt->track.tilt_angle); - - if (move_x_degrees != 0) - cnt->track.pan_angle += -pan.s16.pan / INCPANTILT; - - - if (move_y_degrees != 0) - cnt->track.tilt_angle += -pan.s16.tilt / INCPANTILT; - - if (debug_level >= TRACK_DEBUG) - motion_log(LOG_DEBUG, 0, "After_REL_Y_Angel : x= %d , Y= %d", - cnt->track.pan_angle, cnt->track.tilt_angle); - } - - return cnt->track.move_wait; + /* RELATIVE MOVING : Act.Position +/- X and Y */ + + int delta_x = cent->x - (imgs->width / 2); + int delta_y = cent->y - (imgs->height / 2); + int move_x_degrees, move_y_degrees; + + /* DWe 30.03.07 Does the request of act.position from WebCam work ? luvcview shows at every position 180 :( */ + /* Now we init the Web by call Reset, so we can sure, that we are at x/y = 0,0 */ + /* Don't worry, if the WebCam make a sound - over End at PAN - hmmm, should it be normal ...? */ + /* PAN Value 7777 in relative will init also a want reset for CAM - it will be "0" after that */ + if ((cnt->track.minmaxfound != 1) || (cent->x == 7777)) { + unsigned short int reset = 3; //0-non reset, 1-reset pan, 2-reset tilt, 3-reset pan&tilt + struct v4l2_control control_s; + + control_s.id = V4L2_CID_PANTILT_RESET; + control_s.value = (unsigned char) reset; + + if (ioctl(dev, VIDIOC_S_CTRL, &control_s) < 0) { + motion_log(LOG_ERR, 1, "%s: Failed to reset UVC camera to starting position! Reason", + __FUNCTION__); + return 0; + } + + if (debug_level >= TRACK_DEBUG) + motion_log(LOG_DEBUG, 0, "%s: Reseting UVC camera to starting position", __FUNCTION__); + + /* set the "helpvalue" back to null because after reset CAM should be in x=0 and not 70 */ + cent->x = 0; + SLEEP(8, 0); + + /* DWe 30.03.07 The orig request failed : + * must be VIDIOC_G_CTRL separate for pan and tilt or via VIDIOC_G_EXT_CTRLS - now for 1st manual + * Range X = -70 to +70 degrees + * Y = -30 to +30 degrees + */ + + cnt->track.minx = -4480 / INCPANTILT; + cnt->track.miny = -1920 / INCPANTILT; + cnt->track.maxx = 4480 / INCPANTILT; + cnt->track.maxy = 1920 / INCPANTILT; + cnt->track.dev = dev; + cnt->track.pan_angle = 0; + cnt->track.tilt_angle = 0; + cnt->track.minmaxfound = 1; + } + + + /* If we are on auto track we calculate delta, otherwise we use user input in degrees */ + if (!manual) { + if (delta_x > imgs->width * 3/8 && delta_x < imgs->width * 5/8) + return 0; + if (delta_y > imgs->height * 3/8 && delta_y < imgs->height * 5/8) + return 0; + + move_x_degrees = delta_x * cnt->track.step_angle_x / (imgs->width / 2); + move_y_degrees = -delta_y * cnt->track.step_angle_y / (imgs->height / 2); + } else { + move_x_degrees = cent->x; + move_y_degrees = cent->y; + } + + union pantilt { + struct { + short pan; + short tilt; + } s16; + int value; + }; + + struct v4l2_control control_s; + union pantilt pan; + + if (cnt->track.minmaxfound == 1) { + /* + * Check current position of camera and see if we need to adjust + * values down to what is left to move + */ + if (move_x_degrees < 0 && (cnt->track.minx - cnt->track.pan_angle) > move_x_degrees) + move_x_degrees = cnt->track.minx - cnt->track.pan_angle; + + if (move_x_degrees > 0 && (cnt->track.maxx - cnt->track.pan_angle) < move_x_degrees) + move_x_degrees = cnt->track.maxx - cnt->track.pan_angle; + + if (move_y_degrees < 0 && (cnt->track.miny - cnt->track.tilt_angle) > move_y_degrees) + move_y_degrees = cnt->track.miny - cnt->track.tilt_angle; + + if (move_y_degrees > 0 && (cnt->track.maxy - cnt->track.tilt_angle) < move_y_degrees) + move_y_degrees = cnt->track.maxy - cnt->track.tilt_angle; + } + + if (debug_level >= TRACK_DEBUG) { + motion_log(LOG_DEBUG, 0, "For_SET_REL pan_min %d,pan_max %d,tilt_min %d,tilt_max %d", + cnt->track.minx, cnt->track.maxx, cnt->track.miny, cnt->track.maxy); + motion_log(LOG_DEBUG, 0, "For_SET_REL track_pan_Angel %d, track_tilt_Angel %d", + cnt->track.pan_angle, cnt->track.tilt_angle); + motion_log(LOG_DEBUG, 0, "For_SET_REL move_X %d,move_Y %d", move_x_degrees, move_y_degrees); + } + + /* + tilt up: - value + tilt down: + value + pan left: - value + pan right: + value + */ + + pan.s16.pan = -move_x_degrees * INCPANTILT; + pan.s16.tilt = -move_y_degrees * INCPANTILT; + + /* DWe 30.03.07 Must be broken in diff calls, because + - one call for both is not accept via VIDIOC_S_CTRL -> maybe via VIDIOC_S_EXT_CTRLS + - The Webcam or uvcvideo does not like a call with a zero-move + */ + + if (move_x_degrees != 0) { + + control_s.id = V4L2_CID_PAN_RELATIVE; + + control_s.value = pan.s16.pan; + if (debug_level >= TRACK_DEBUG) + motion_log(LOG_DEBUG, 0, " dev %d, addr= %d, control_S= %d, Wert= %d", + dev, VIDIOC_S_CTRL, &control_s, pan.s16.pan); + + if (ioctl(dev, VIDIOC_S_CTRL, &control_s) < 0) { + motion_log(LOG_ERR, 1, "%s: Failed to move UVC camera!", __FUNCTION__); + return 0; + } + } + + /* DWe 30.03.07 We must wait a little,before we set the next CMD, otherwise PAN is mad ... */ + if ((move_x_degrees != 0) && (move_y_degrees != 0)) + SLEEP (1, 0); + + + if (move_y_degrees != 0) { + + control_s.id = V4L2_CID_TILT_RELATIVE; + + control_s.value = pan.s16.tilt; + if (debug_level >= TRACK_DEBUG) + motion_log(LOG_DEBUG, 0, " dev %d,addr= %d, control_S= %d, Wert= %d", + dev, VIDIOC_S_CTRL, &control_s, pan.s16.tilt); + + if (ioctl(dev, VIDIOC_S_CTRL, &control_s) < 0) { + motion_log(LOG_ERR, 1, "%s: Failed to move UVC camera!", __FUNCTION__); + return 0; + } + } + + if (debug_level >= TRACK_DEBUG) + motion_log(LOG_DEBUG, 0, "%s: Found MINMAX = %d", + __FUNCTION__, cnt->track.minmaxfound); + + if (cnt->track.minmaxfound == 1) { + if (debug_level >= TRACK_DEBUG) + motion_log(LOG_DEBUG, 0, "Before_REL_Y_Angel : x= %d , Y= %d", + cnt->track.pan_angle, cnt->track.tilt_angle); + + if (move_x_degrees != 0) + cnt->track.pan_angle += -pan.s16.pan / INCPANTILT; + + + if (move_y_degrees != 0) + cnt->track.tilt_angle += -pan.s16.tilt / INCPANTILT; + + if (debug_level >= TRACK_DEBUG) + motion_log(LOG_DEBUG, 0, "After_REL_Y_Angel : x= %d , Y= %d", + cnt->track.pan_angle, cnt->track.tilt_angle); + } + + return cnt->track.move_wait; } #endif /* MOTION_V4L2 */ #endif /* WITHOUT_V4L */ diff --git a/video.c b/video.c index 9db3987..fa44fb3 100644 --- a/video.c +++ b/video.c @@ -1,9 +1,10 @@ -/* video.c +/* + * video.c * - * Video stream functions for motion. - * Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org) - * This software is distributed under the GNU public license version 2 - * See also the file 'COPYING'. + * Video stream functions for motion. + * Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org) + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. * */ #ifndef WITHOUT_V4L @@ -12,232 +13,235 @@ #include "rotate.h" /* already includes motion.h */ #include "video.h" - -/* for the v4l stuff: */ -#include -#include +/* for vloopback */ #include #include static void v4l_picture_controls(struct context *cnt, struct video_dev *viddev) { - int dev = viddev->fd; - struct video_picture vid_pic; - int make_change = 0; - - if (cnt->conf.contrast && cnt->conf.contrast != viddev->contrast) { - - if (ioctl(dev, VIDIOCGPICT, &vid_pic) == -1) - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGPICT)", __FUNCTION__); - - make_change = 1; - vid_pic.contrast = cnt->conf.contrast * 256; - viddev->contrast = cnt->conf.contrast; - } - - if (cnt->conf.saturation && cnt->conf.saturation != viddev->saturation) { - - if (!make_change) { - if (ioctl(dev, VIDIOCGPICT, &vid_pic)==-1) - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGPICT)", __FUNCTION__); - } - - make_change = 1; - vid_pic.colour = cnt->conf.saturation * 256; - viddev->saturation = cnt->conf.saturation; - } - - if (cnt->conf.hue && cnt->conf.hue != viddev->hue) { - - if (!make_change) { - if (ioctl(dev, VIDIOCGPICT, &vid_pic) == -1) - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGPICT)", __FUNCTION__); - } - - make_change = 1; - vid_pic.hue = cnt->conf.hue * 256; - viddev->hue = cnt->conf.hue; - } - - if (cnt->conf.autobright) { - - if (vid_do_autobright(cnt, viddev)) { - /* If we already read the VIDIOGPICT - we should not do it again */ - if (!make_change) { - if (ioctl(dev, VIDIOCGPICT, &vid_pic) == -1) - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGPICT)", __FUNCTION__); - } - - vid_pic.brightness = viddev->brightness * 256; - make_change = 1; - } - - } else { - if (cnt->conf.brightness && cnt->conf.brightness != viddev->brightness) { - if (!make_change) { - if (ioctl(dev, VIDIOCGPICT, &vid_pic) == -1) - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGPICT)", __FUNCTION__); - } - - make_change = 1; - vid_pic.brightness = cnt->conf.brightness * 256; - viddev->brightness = cnt->conf.brightness; - } - } - - if (make_change) { - if (ioctl(dev, VIDIOCSPICT, &vid_pic) == -1) - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCSPICT)", __FUNCTION__); - } + int dev = viddev->fd; + struct video_picture vid_pic; + int make_change = 0; + + if (cnt->conf.contrast && cnt->conf.contrast != viddev->contrast) { + + if (ioctl(dev, VIDIOCGPICT, &vid_pic) == -1) + motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGPICT)", __FUNCTION__); + + make_change = 1; + vid_pic.contrast = cnt->conf.contrast * 256; + viddev->contrast = cnt->conf.contrast; + } + + if (cnt->conf.saturation && cnt->conf.saturation != viddev->saturation) { + + if (!make_change) { + if (ioctl(dev, VIDIOCGPICT, &vid_pic)==-1) + motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGPICT)", __FUNCTION__); + } + + make_change = 1; + vid_pic.colour = cnt->conf.saturation * 256; + viddev->saturation = cnt->conf.saturation; + } + + if (cnt->conf.hue && cnt->conf.hue != viddev->hue) { + + if (!make_change) { + if (ioctl(dev, VIDIOCGPICT, &vid_pic) == -1) + motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGPICT)", __FUNCTION__); + } + + make_change = 1; + vid_pic.hue = cnt->conf.hue * 256; + viddev->hue = cnt->conf.hue; + } + + if (cnt->conf.autobright) { + + if (vid_do_autobright(cnt, viddev)) { + /* If we already read the VIDIOGPICT - we should not do it again */ + if (!make_change) { + if (ioctl(dev, VIDIOCGPICT, &vid_pic) == -1) + motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGPICT)", __FUNCTION__); + } + + vid_pic.brightness = viddev->brightness * 256; + make_change = 1; + } + + } else { + if (cnt->conf.brightness && cnt->conf.brightness != viddev->brightness) { + if (!make_change) { + if (ioctl(dev, VIDIOCGPICT, &vid_pic) == -1) + motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGPICT)", __FUNCTION__); + } + + make_change = 1; + vid_pic.brightness = cnt->conf.brightness * 256; + viddev->brightness = cnt->conf.brightness; + } + } + + if (make_change) { + if (ioctl(dev, VIDIOCSPICT, &vid_pic) == -1) + motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCSPICT)", __FUNCTION__); + } } /******************************************************************************************* - Video4linux capture routines + Video4linux capture routines */ unsigned char *v4l_start(struct video_dev *viddev, int width, int height,int input, int norm, unsigned long freq, int tuner_number) { - int dev = viddev->fd; - struct video_capability vid_caps; - struct video_channel vid_chnl; - struct video_tuner vid_tuner; - struct video_mbuf vid_buf; - struct video_mmap vid_mmap; - void *map; - - if (ioctl (dev, VIDIOCGCAP, &vid_caps) == -1) { - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGCAP)", __FUNCTION__); - return (NULL); - } - - if (vid_caps.type & VID_TYPE_MONOCHROME) - viddev->v4l_fmt = VIDEO_PALETTE_GREY; - - if (input != IN_DEFAULT) { - memset(&vid_chnl, 0, sizeof(struct video_channel)); - vid_chnl.channel = input; - - if (ioctl (dev, VIDIOCGCHAN, &vid_chnl) == -1) { - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGCHAN)", __FUNCTION__); - } else { - vid_chnl.channel = input; - vid_chnl.norm = norm; - if (ioctl (dev, VIDIOCSCHAN, &vid_chnl) == -1) { - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCSCHAN)", __FUNCTION__); - return (NULL); - } - } - } - - if (freq) { - memset(&vid_tuner, 0, sizeof(struct video_tuner)); - vid_tuner.tuner = tuner_number; - if (ioctl (dev, VIDIOCGTUNER, &vid_tuner) == -1) { - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGTUNER)", __FUNCTION__); - } else { - if (vid_tuner.flags & VIDEO_TUNER_LOW) { - freq = freq*16; /* steps of 1/16 KHz */ - } else { - freq = (freq*10)/625; - } - if (ioctl(dev, VIDIOCSFREQ, &freq) == -1) { - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCSFREQ)", __FUNCTION__); - return (NULL); - } - if (debug_level >= CAMERA_VERBOSE) - motion_log(-1, 0, "%s: Frequency set", __FUNCTION__); - } - } - - if (ioctl (dev, VIDIOCGMBUF, &vid_buf) == -1) { - motion_log(LOG_ERR, 0, "%s: ioctl(VIDIOCGMBUF) - Error device does not support memory map\n", - "V4L capturing using read is deprecated!\nMotion only supports mmap.", __FUNCTION__); - return NULL; - } else { - map = mmap(0, vid_buf.size, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0); - viddev->size_map = vid_buf.size; - if (vid_buf.frames > 1) { - viddev->v4l_maxbuffer = 2; - viddev->v4l_buffers[0] = map; - viddev->v4l_buffers[1] = (unsigned char *)map + vid_buf.offsets[1]; - } else { - viddev->v4l_buffers[0] = map; - viddev->v4l_maxbuffer = 1; - } - - if (MAP_FAILED == map) { - motion_log(LOG_ERR, 1, "%s: MAP_FAILED", __FUNCTION__); - return (NULL); - } - viddev->v4l_curbuffer = 0; - vid_mmap.format = viddev->v4l_fmt; - vid_mmap.frame = viddev->v4l_curbuffer; - vid_mmap.width = width; - vid_mmap.height = height; - if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { - motion_log(LOG_DEBUG, 1, "%s: Failed with YUV420P, trying YUV422 palette", - __FUNCTION__); - viddev->v4l_fmt = VIDEO_PALETTE_YUV422; - vid_mmap.format = viddev->v4l_fmt; - /* Try again... */ - if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { - motion_log(LOG_DEBUG, 1, "%s: Failed with YUV422, trying YUYV palette", - __FUNCTION__); - viddev->v4l_fmt = VIDEO_PALETTE_YUYV; - vid_mmap.format = viddev->v4l_fmt; - - if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { - motion_log(LOG_DEBUG, 1, "%s: Failed with YUYV, trying RGB24 palette", - __FUNCTION__); - viddev->v4l_fmt = VIDEO_PALETTE_RGB24; - vid_mmap.format = viddev->v4l_fmt; - /* Try again... */ - - if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { - motion_log(LOG_DEBUG, 1, "%s: Failed with RGB24, trying" - "GREYSCALE palette", __FUNCTION__); - viddev->v4l_fmt = VIDEO_PALETTE_GREY; - vid_mmap.format = viddev->v4l_fmt; - /* Try one last time... */ - if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { - motion_log(LOG_ERR, 1, "%s: Failed with all supported palettes " - "- giving up", __FUNCTION__); - return (NULL); - } - } - } - } - } - } - - switch (viddev->v4l_fmt) { - case VIDEO_PALETTE_YUV420P: - viddev->v4l_bufsize = (width * height * 3) / 2; - motion_log(LOG_DEBUG, 0, "%s: Using VIDEO_PALETTE_YUV420P palette", __FUNCTION__); - break; - case VIDEO_PALETTE_YUV422: - viddev->v4l_bufsize = (width * height * 2); - motion_log(LOG_DEBUG, 0, "%s: Using VIDEO_PALETTE_YUV422 palette", __FUNCTION__); - break; - case VIDEO_PALETTE_YUYV: - viddev->v4l_bufsize = (width * height * 2); - motion_log(LOG_DEBUG, 0, "%s: Using VIDEO_PALETTE_YUYV palette", __FUNCTION__); - break; - case VIDEO_PALETTE_RGB24: - viddev->v4l_bufsize = (width * height * 3); - motion_log(LOG_DEBUG, 0, "%s: Using VIDEO_PALETTE_RGB24 palette", __FUNCTION__); - break; - case VIDEO_PALETTE_GREY: - viddev->v4l_bufsize = width * height; - motion_log(LOG_DEBUG, 0, "%s: Using VIDEO_PALETTE_GREY palette", __FUNCTION__); - break; - } - return map; + int dev = viddev->fd; + struct video_capability vid_caps; + struct video_channel vid_chnl; + struct video_tuner vid_tuner; + struct video_mbuf vid_buf; + struct video_mmap vid_mmap; + void *map; + + if (ioctl (dev, VIDIOCGCAP, &vid_caps) == -1) { + motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGCAP)", __FUNCTION__); + return (NULL); + } + + if (vid_caps.type & VID_TYPE_MONOCHROME) + viddev->v4l_fmt = VIDEO_PALETTE_GREY; + + if (input != IN_DEFAULT) { + memset(&vid_chnl, 0, sizeof(struct video_channel)); + vid_chnl.channel = input; + + if (ioctl (dev, VIDIOCGCHAN, &vid_chnl) == -1) { + motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGCHAN)", __FUNCTION__); + } else { + vid_chnl.channel = input; + vid_chnl.norm = norm; + if (ioctl (dev, VIDIOCSCHAN, &vid_chnl) == -1) { + motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCSCHAN)", __FUNCTION__); + return (NULL); + } + } + } + + if (freq) { + memset(&vid_tuner, 0, sizeof(struct video_tuner)); + vid_tuner.tuner = tuner_number; + if (ioctl (dev, VIDIOCGTUNER, &vid_tuner) == -1) { + motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGTUNER)", __FUNCTION__); + } else { + if (vid_tuner.flags & VIDEO_TUNER_LOW) + freq = freq*16; /* steps of 1/16 KHz */ + else + freq = (freq*10)/625; + + + if (ioctl(dev, VIDIOCSFREQ, &freq) == -1) { + motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCSFREQ)", __FUNCTION__); + return (NULL); + } + + if (debug_level >= CAMERA_VERBOSE) + motion_log(-1, 0, "%s: Frequency set", __FUNCTION__); + } + } + + if (ioctl (dev, VIDIOCGMBUF, &vid_buf) == -1) { + motion_log(LOG_ERR, 0, "%s: ioctl(VIDIOCGMBUF) - Error device does not support memory map\n", + "V4L capturing using read is deprecated!\nMotion only supports mmap.", __FUNCTION__); + return NULL; + } else { + map = mmap(0, vid_buf.size, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0); + viddev->size_map = vid_buf.size; + + if (vid_buf.frames > 1) { + viddev->v4l_maxbuffer = 2; + viddev->v4l_buffers[0] = map; + viddev->v4l_buffers[1] = (unsigned char *)map + vid_buf.offsets[1]; + } else { + viddev->v4l_buffers[0] = map; + viddev->v4l_maxbuffer = 1; + } + + if (MAP_FAILED == map) { + motion_log(LOG_ERR, 1, "%s: MAP_FAILED", __FUNCTION__); + return (NULL); + } + + viddev->v4l_curbuffer = 0; + vid_mmap.format = viddev->v4l_fmt; + vid_mmap.frame = viddev->v4l_curbuffer; + vid_mmap.width = width; + vid_mmap.height = height; + + if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { + motion_log(LOG_DEBUG, 1, "%s: Failed with YUV420P, trying YUV422 palette", + __FUNCTION__); + viddev->v4l_fmt = VIDEO_PALETTE_YUV422; + vid_mmap.format = viddev->v4l_fmt; + /* Try again... */ + if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { + motion_log(LOG_DEBUG, 1, "%s: Failed with YUV422, trying YUYV palette", + __FUNCTION__); + viddev->v4l_fmt = VIDEO_PALETTE_YUYV; + vid_mmap.format = viddev->v4l_fmt; + + if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { + motion_log(LOG_DEBUG, 1, "%s: Failed with YUYV, trying RGB24 palette", + __FUNCTION__); + viddev->v4l_fmt = VIDEO_PALETTE_RGB24; + vid_mmap.format = viddev->v4l_fmt; + /* Try again... */ + + if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { + motion_log(LOG_DEBUG, 1, "%s: Failed with RGB24, trying" + "GREYSCALE palette", __FUNCTION__); + viddev->v4l_fmt = VIDEO_PALETTE_GREY; + vid_mmap.format = viddev->v4l_fmt; + /* Try one last time... */ + if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { + motion_log(LOG_ERR, 1, "%s: Failed with all supported palettes " + "- giving up", __FUNCTION__); + return (NULL); + } + } + } + } + } + } + + switch (viddev->v4l_fmt) { + case VIDEO_PALETTE_YUV420P: + viddev->v4l_bufsize = (width * height * 3) / 2; + motion_log(LOG_DEBUG, 0, "%s: Using VIDEO_PALETTE_YUV420P palette", __FUNCTION__); + break; + case VIDEO_PALETTE_YUV422: + viddev->v4l_bufsize = (width * height * 2); + motion_log(LOG_DEBUG, 0, "%s: Using VIDEO_PALETTE_YUV422 palette", __FUNCTION__); + break; + case VIDEO_PALETTE_YUYV: + viddev->v4l_bufsize = (width * height * 2); + motion_log(LOG_DEBUG, 0, "%s: Using VIDEO_PALETTE_YUYV palette", __FUNCTION__); + break; + case VIDEO_PALETTE_RGB24: + viddev->v4l_bufsize = (width * height * 3); + motion_log(LOG_DEBUG, 0, "%s: Using VIDEO_PALETTE_RGB24 palette", __FUNCTION__); + break; + case VIDEO_PALETTE_GREY: + viddev->v4l_bufsize = width * height; + motion_log(LOG_DEBUG, 0, "%s: Using VIDEO_PALETTE_GREY palette", __FUNCTION__); + break; + } + + return map; } @@ -259,307 +263,335 @@ unsigned char *v4l_start(struct video_dev *viddev, int width, int height,int inp */ int v4l_next(struct video_dev *viddev, unsigned char *map, int width, int height) { - int dev = viddev->fd; - int frame = viddev->v4l_curbuffer; - struct video_mmap vid_mmap; - unsigned char *cap_map; - - sigset_t set, old; - - /* MMAP method is used */ - vid_mmap.format = viddev->v4l_fmt; - vid_mmap.width = width; - vid_mmap.height = height; - - /* Block signals during IOCTL */ - sigemptyset(&set); - sigaddset(&set, SIGCHLD); - sigaddset(&set, SIGALRM); - sigaddset(&set, SIGUSR1); - sigaddset(&set, SIGTERM); - sigaddset(&set, SIGHUP); - pthread_sigmask (SIG_BLOCK, &set, &old); - - cap_map = viddev->v4l_buffers[viddev->v4l_curbuffer]; - viddev->v4l_curbuffer++; - - if (viddev->v4l_curbuffer >= viddev->v4l_maxbuffer) - viddev->v4l_curbuffer = 0; - - vid_mmap.frame = viddev->v4l_curbuffer; - - if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { - motion_log(LOG_ERR, 1, "%s: mcapture error in proc %d", __FUNCTION__, getpid()); - sigprocmask (SIG_UNBLOCK, &old, NULL); - return V4L_FATAL_ERROR; - } - - vid_mmap.frame = frame; - - if (ioctl(dev, VIDIOCSYNC, &vid_mmap.frame) == -1) { - motion_log(LOG_ERR, 1, "%s: sync error in proc %d", __FUNCTION__, getpid()); - sigprocmask (SIG_UNBLOCK, &old, NULL); - } - - pthread_sigmask (SIG_UNBLOCK, &old, NULL); /*undo the signal blocking*/ - - switch (viddev->v4l_fmt) { - case VIDEO_PALETTE_RGB24: - conv_rgb24toyuv420p(map, cap_map, width, height); - break; - case VIDEO_PALETTE_YUYV: - case VIDEO_PALETTE_YUV422: - conv_yuv422to420p(map, cap_map, width, height); - break; - default: - memcpy(map, cap_map, viddev->v4l_bufsize); - } - - return 0; + int dev = viddev->fd; + int frame = viddev->v4l_curbuffer; + struct video_mmap vid_mmap; + unsigned char *cap_map; + + sigset_t set, old; + + /* MMAP method is used */ + vid_mmap.format = viddev->v4l_fmt; + vid_mmap.width = width; + vid_mmap.height = height; + + /* Block signals during IOCTL */ + sigemptyset(&set); + sigaddset(&set, SIGCHLD); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGUSR1); + sigaddset(&set, SIGTERM); + sigaddset(&set, SIGHUP); + pthread_sigmask (SIG_BLOCK, &set, &old); + + cap_map = viddev->v4l_buffers[viddev->v4l_curbuffer]; + viddev->v4l_curbuffer++; + + if (viddev->v4l_curbuffer >= viddev->v4l_maxbuffer) + viddev->v4l_curbuffer = 0; + + vid_mmap.frame = viddev->v4l_curbuffer; + + if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { + motion_log(LOG_ERR, 1, "%s: mcapture error in proc %d", __FUNCTION__, getpid()); + sigprocmask (SIG_UNBLOCK, &old, NULL); + return V4L_FATAL_ERROR; + } + + vid_mmap.frame = frame; + + if (ioctl(dev, VIDIOCSYNC, &vid_mmap.frame) == -1) { + motion_log(LOG_ERR, 1, "%s: sync error in proc %d", __FUNCTION__, getpid()); + sigprocmask (SIG_UNBLOCK, &old, NULL); + } + + pthread_sigmask (SIG_UNBLOCK, &old, NULL); /*undo the signal blocking*/ + + switch (viddev->v4l_fmt) { + case VIDEO_PALETTE_RGB24: + conv_rgb24toyuv420p(map, cap_map, width, height); + break; + case VIDEO_PALETTE_YUYV: + case VIDEO_PALETTE_YUV422: + conv_yuv422to420p(map, cap_map, width, height); + break; + default: + memcpy(map, cap_map, viddev->v4l_bufsize); + } + + return 0; } void v4l_set_input(struct context *cnt, struct video_dev *viddev, unsigned char *map, int width, int height, int input, int norm, int skip, unsigned long freq, int tuner_number) { - int dev = viddev->fd; - int i; - struct video_channel vid_chnl; - struct video_tuner vid_tuner; - unsigned long frequnits = freq; - - if (input != viddev->input || width != viddev->width || height != viddev->height || - freq != viddev->freq || tuner_number != viddev->tuner_number) { - if (freq) { - - memset(&vid_tuner, 0, sizeof(struct video_tuner)); - - vid_tuner.tuner = tuner_number; - if (ioctl (dev, VIDIOCGTUNER, &vid_tuner) == -1) { - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGTUNER)", __FUNCTION__); - } else { - if (vid_tuner.flags & VIDEO_TUNER_LOW) { - frequnits = freq*16; /* steps of 1/16 KHz */ - } else { - frequnits = (freq*10)/625; - } - if (ioctl(dev, VIDIOCSFREQ, &frequnits) == -1) { - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCSFREQ)", __FUNCTION__); - return; - } - } - } - - memset(&vid_chnl, 0, sizeof(struct video_channel)); - vid_chnl.channel = input; - - if (ioctl (dev, VIDIOCGCHAN, &vid_chnl) == -1) { - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGCHAN)", __FUNCTION__); - } else { - vid_chnl.channel = input; - vid_chnl.norm = norm; - if (ioctl (dev, VIDIOCSCHAN, &vid_chnl) == -1) { - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCSCHAN)", __FUNCTION__); - return; - } - } - v4l_picture_controls(cnt, viddev); - viddev->input = input; - viddev->width = width; - viddev->height = height; - viddev->freq = freq; - viddev->tuner_number = tuner_number; - /* skip a few frames if needed */ - for (i = 0; i < skip; i++) - v4l_next(viddev, map, width, height); - } else { - /* No round robin - we only adjust picture controls */ - v4l_picture_controls(cnt, viddev); - } + int dev = viddev->fd; + int i; + struct video_channel vid_chnl; + struct video_tuner vid_tuner; + unsigned long frequnits = freq; + + if (input != viddev->input || width != viddev->width || height != viddev->height || + freq != viddev->freq || tuner_number != viddev->tuner_number) { + if (freq) { + + memset(&vid_tuner, 0, sizeof(struct video_tuner)); + vid_tuner.tuner = tuner_number; + + if (ioctl (dev, VIDIOCGTUNER, &vid_tuner) == -1) { + motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGTUNER)", __FUNCTION__); + } else { + if (vid_tuner.flags & VIDEO_TUNER_LOW) + frequnits = freq*16; /* steps of 1/16 KHz */ + else + frequnits = (freq*10)/625; + + if (ioctl(dev, VIDIOCSFREQ, &frequnits) == -1) { + motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCSFREQ)", __FUNCTION__); + return; + } + } + } + + memset(&vid_chnl, 0, sizeof(struct video_channel)); + vid_chnl.channel = input; + + if (ioctl (dev, VIDIOCGCHAN, &vid_chnl) == -1) { + motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGCHAN)", __FUNCTION__); + } else { + vid_chnl.channel = input; + vid_chnl.norm = norm; + if (ioctl (dev, VIDIOCSCHAN, &vid_chnl) == -1) { + motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCSCHAN)", __FUNCTION__); + return; + } + } + + v4l_picture_controls(cnt, viddev); + viddev->input = input; + viddev->width = width; + viddev->height = height; + viddev->freq = freq; + viddev->tuner_number = tuner_number; + /* skip a few frames if needed */ + for (i = 0; i < skip; i++) + v4l_next(viddev, map, width, height); + } else { + /* No round robin - we only adjust picture controls */ + v4l_picture_controls(cnt, viddev); + } } static int v4l_open_vidpipe(void) { - int pipe_fd = -1; - char pipepath[255]; - char buffer[255]; - char *major; - char *minor; - struct utsname uts; - - if (uname(&uts) < 0) { - motion_log(LOG_ERR, 1, "%s: Unable to execute uname", __FUNCTION__); - return -1; - } - major = strtok(uts.release, "."); - minor = strtok(NULL, "."); - if ((major == NULL) || (minor == NULL) || (strcmp(major, "2"))) { - motion_log(LOG_ERR, 1, "%s: Unable to decipher OS version", __FUNCTION__); - return -1; - } - if (strcmp(minor, "5") < 0) { - FILE *vloopbacks; - char *loop; - char *input; - char *istatus; - char *output; - char *ostatus; - - vloopbacks = fopen("/proc/video/vloopback/vloopbacks", "r"); - if (!vloopbacks) { - motion_log(LOG_ERR, 1, "%s: Failed to open '/proc/video/vloopback/vloopbacks'", - __FUNCTION__); - return -1; - } - - /* Read vloopback version*/ - if (!fgets(buffer, 255, vloopbacks)) { - motion_log(LOG_ERR, 1, "%s: Unable to read vloopback version", __FUNCTION__); - return -1; - } - - fprintf(stderr, "\t%s", buffer); - - /* Read explanation line */ - - if (!fgets(buffer, 255, vloopbacks)) { - motion_log(LOG_ERR, 1, "%s: Unable to read vloopback explanation line", - __FUNCTION__); - return -1; - } - - while (fgets(buffer, 255, vloopbacks)) { - if (strlen(buffer) > 1) { - buffer[strlen(buffer)-1] = 0; - loop = strtok(buffer, "\t"); - input = strtok(NULL, "\t"); - istatus = strtok(NULL, "\t"); - output = strtok(NULL, "\t"); - ostatus = strtok(NULL, "\t"); - if (istatus[0] == '-') { - snprintf(pipepath, 255, "/dev/%s", input); - pipe_fd = open(pipepath, O_RDWR); - if (pipe_fd >= 0) { - motion_log(-1, 0, "%s: \tInput: /dev/%s \tOutput: /dev/%s", - __FUNCTION__, input, output); - break; - } - } - } - } - fclose(vloopbacks); - } else { - DIR *dir; - struct dirent *dirp; - const char prefix[] = "/sys/class/video4linux/"; - char *ptr, *io; - int fd; - int low = 9999; - int tfd; - int tnum; - - if ((dir = opendir(prefix)) == NULL) { - motion_log(LOG_ERR, 1, "%s: Failed to open '%s'", __FUNCTION__, prefix); - return -1; - } - while ((dirp = readdir(dir)) != NULL) { - if (!strncmp(dirp->d_name, "video", 5)) { - strncpy(buffer, prefix, 255); - strncat(buffer, dirp->d_name, 255); - strncat(buffer, "/name", 255); - if ((fd = open(buffer, O_RDONLY)) >= 0) { - if ((read(fd, buffer, sizeof(buffer)-1)) < 0) { - close(fd); - continue; - } - ptr = strtok(buffer, " "); - if (strcmp(ptr, "Video")) { - close(fd); - continue; - } - major = strtok(NULL, " "); - minor = strtok(NULL, " "); - io = strtok(NULL, " \n"); - if (strcmp(major, "loopback") || strcmp(io, "input")) { - close(fd); - continue; - } - if ((ptr = strtok(buffer, " ")) == NULL) { - close(fd); - continue; - } - tnum = atoi(minor); - if (tnum < low) { - strcpy(buffer, "/dev/"); - strcat(buffer, dirp->d_name); - if ((tfd = open(buffer, O_RDWR)) >= 0) { - strcpy(pipepath, buffer); - if (pipe_fd >= 0) { - close(pipe_fd); - } - pipe_fd = tfd; - low = tnum; - } - } - close(fd); - } - } - } - closedir(dir); - if (pipe_fd >= 0) - motion_log(-1, 0, "%s: Opened input of %s", __FUNCTION__, pipepath); - } - return pipe_fd; + int pipe_fd = -1; + char pipepath[255]; + char buffer[255]; + char *major; + char *minor; + struct utsname uts; + + if (uname(&uts) < 0) { + motion_log(LOG_ERR, 1, "%s: Unable to execute uname", __FUNCTION__); + return -1; + } + + major = strtok(uts.release, "."); + minor = strtok(NULL, "."); + + if ((major == NULL) || (minor == NULL) || (strcmp(major, "2"))) { + motion_log(LOG_ERR, 1, "%s: Unable to decipher OS version", __FUNCTION__); + return -1; + } + + if (strcmp(minor, "5") < 0) { + FILE *vloopbacks; + char *loop; + char *input; + char *istatus; + char *output; + char *ostatus; + + vloopbacks = fopen("/proc/video/vloopback/vloopbacks", "r"); + + if (!vloopbacks) { + motion_log(LOG_ERR, 1, "%s: Failed to open '/proc/video/vloopback/vloopbacks'", + __FUNCTION__); + return -1; + } + + /* Read vloopback version*/ + if (!fgets(buffer, 255, vloopbacks)) { + motion_log(LOG_ERR, 1, "%s: Unable to read vloopback version", __FUNCTION__); + return -1; + } + + fprintf(stderr, "\t%s", buffer); + + /* Read explanation line */ + + if (!fgets(buffer, 255, vloopbacks)) { + motion_log(LOG_ERR, 1, "%s: Unable to read vloopback explanation line", + __FUNCTION__); + return -1; + } + + while (fgets(buffer, 255, vloopbacks)) { + if (strlen(buffer) > 1) { + buffer[strlen(buffer)-1] = 0; + loop = strtok(buffer, "\t"); + input = strtok(NULL, "\t"); + istatus = strtok(NULL, "\t"); + output = strtok(NULL, "\t"); + ostatus = strtok(NULL, "\t"); + + if (istatus[0] == '-') { + snprintf(pipepath, 255, "/dev/%s", input); + pipe_fd = open(pipepath, O_RDWR); + + if (pipe_fd >= 0) { + motion_log(-1, 0, "%s: \tInput: /dev/%s \tOutput: /dev/%s", + __FUNCTION__, input, output); + break; + } + } + } + } + + fclose(vloopbacks); + } else { + DIR *dir; + struct dirent *dirp; + const char prefix[] = "/sys/class/video4linux/"; + char *ptr, *io; + int fd; + int low = 9999; + int tfd; + int tnum; + + if ((dir = opendir(prefix)) == NULL) { + motion_log(LOG_ERR, 1, "%s: Failed to open '%s'", __FUNCTION__, prefix); + return -1; + } + + while ((dirp = readdir(dir)) != NULL) { + if (!strncmp(dirp->d_name, "video", 5)) { + strncpy(buffer, prefix, 255); + strncat(buffer, dirp->d_name, 255); + strncat(buffer, "/name", 255); + + if ((fd = open(buffer, O_RDONLY)) >= 0) { + if ((read(fd, buffer, sizeof(buffer)-1)) < 0) { + close(fd); + continue; + } + + ptr = strtok(buffer, " "); + + if (strcmp(ptr, "Video")) { + close(fd); + continue; + } + + major = strtok(NULL, " "); + minor = strtok(NULL, " "); + io = strtok(NULL, " \n"); + + if (strcmp(major, "loopback") || strcmp(io, "input")) { + close(fd); + continue; + } + + if ((ptr = strtok(buffer, " ")) == NULL) { + close(fd); + continue; + } + + tnum = atoi(minor); + + if (tnum < low) { + strcpy(buffer, "/dev/"); + strcat(buffer, dirp->d_name); + if ((tfd = open(buffer, O_RDWR)) >= 0) { + strcpy(pipepath, buffer); + + if (pipe_fd >= 0) + close(pipe_fd); + + pipe_fd = tfd; + low = tnum; + } + } + close(fd); + } + } + } + + closedir(dir); + + if (pipe_fd >= 0) + motion_log(-1, 0, "%s: Opened input of %s", __FUNCTION__, pipepath); + } + + return pipe_fd; } static int v4l_startpipe(const char *dev_name, int width, int height, int type) { - int dev; - struct video_picture vid_pic; - struct video_window vid_win; - - if (!strcmp(dev_name, "-")) { - dev = v4l_open_vidpipe(); - } else { - dev = open(dev_name, O_RDWR); - } - if (dev < 0) - return(-1); - - if (ioctl(dev, VIDIOCGPICT, &vid_pic) == -1) { - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGPICT)", __FUNCTION__); - return(-1); - } - vid_pic.palette = type; - if (ioctl(dev, VIDIOCSPICT, &vid_pic) == -1) { - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCSPICT)", __FUNCTION__); - return(-1); - } - if (ioctl(dev, VIDIOCGWIN, &vid_win) == -1) { - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGWIN)", __FUNCTION__); - return(-1); - } - vid_win.height = height; - vid_win.width = width; - if (ioctl(dev, VIDIOCSWIN, &vid_win) == -1) { - motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCSWIN)", __FUNCTION__); - return(-1); - } - return dev; + int dev; + struct video_picture vid_pic; + struct video_window vid_win; + + if (!strcmp(dev_name, "-")) { + dev = v4l_open_vidpipe(); + } else { + dev = open(dev_name, O_RDWR); + } + + if (dev < 0) + return(-1); + + if (ioctl(dev, VIDIOCGPICT, &vid_pic) == -1) { + motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGPICT)", __FUNCTION__); + return(-1); + } + + vid_pic.palette = type; + + if (ioctl(dev, VIDIOCSPICT, &vid_pic) == -1) { + motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCSPICT)", __FUNCTION__); + return(-1); + } + + if (ioctl(dev, VIDIOCGWIN, &vid_win) == -1) { + motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCGWIN)", __FUNCTION__); + return(-1); + } + + vid_win.height = height; + vid_win.width = width; + + if (ioctl(dev, VIDIOCSWIN, &vid_win) == -1) { + motion_log(LOG_ERR, 1, "%s: ioctl (VIDIOCSWIN)", __FUNCTION__); + return(-1); + } + + return dev; } static int v4l_putpipe (int dev, unsigned char *image, int size) { - return write(dev, image, size); + return write(dev, image, size); } int vid_startpipe(const char *dev_name, int width, int height, int type) { - return v4l_startpipe(dev_name, width, height, type); + return v4l_startpipe(dev_name, width, height, type); } int vid_putpipe (int dev, unsigned char *image, int size) { - return v4l_putpipe(dev, image, size); + return v4l_putpipe(dev, image, size); } #endif /*WITHOUT_V4L*/ diff --git a/video.h b/video.h index 13f47a3..125394a 100644 --- a/video.h +++ b/video.h @@ -13,6 +13,7 @@ #define _LINUX_TIME_H 1 #ifndef WITHOUT_V4L #include +#include #endif /* video4linux stuff */ @@ -20,7 +21,7 @@ #define NORM_PAL 0 #define NORM_NTSC 1 #define NORM_SECAM 2 -#define NORM_PAL_NC 3 +#define NORM_PAL_NC 3 #define IN_DEFAULT 8 #define IN_TV 0 #define IN_COMPOSITE 1 @@ -35,37 +36,37 @@ #define VIDEO_DEVICE "/dev/video0" struct video_dev { - struct video_dev *next; - int usage_count; - int fd; - const char *video_device; - int input; - int width; - int height; - int brightness; - int contrast; - int saturation; - int hue; - unsigned long freq; - int tuner_number; + struct video_dev *next; + int usage_count; + int fd; + const char *video_device; + int input; + int width; + int height; + int brightness; + int contrast; + int saturation; + int hue; + unsigned long freq; + int tuner_number; + + pthread_mutex_t mutex; + pthread_mutexattr_t attr; + int owner; + int frames; - pthread_mutex_t mutex; - pthread_mutexattr_t attr; - int owner; - int frames; - - /* Device type specific stuff: */ + /* Device type specific stuff: */ #ifndef WITHOUT_V4L - /* v4l */ - int v4l2; - void *v4l2_private; - - int size_map; - int v4l_fmt; - unsigned char *v4l_buffers[2]; - int v4l_curbuffer; - int v4l_maxbuffer; - int v4l_bufsize; + /* v4l */ + int v4l2; + void *v4l2_private; + + int size_map; + int v4l_fmt; + unsigned char *v4l_buffers[2]; + int v4l_curbuffer; + int v4l_maxbuffer; + int v4l_bufsize; #endif }; @@ -79,7 +80,7 @@ void conv_yuv422to420p(unsigned char *map, unsigned char *cap_map, int width, in void conv_uyvyto420p(unsigned char *map, unsigned char *cap_map, unsigned int width, unsigned int height); void conv_rgb24toyuv420p(unsigned char *map, unsigned char *cap_map, int width, int height); int conv_jpeg2yuv420(struct context *cnt, unsigned char *dst, netcam_buff * buff, int width, int height); -int sonix_decompress(unsigned char *outp, unsigned char *inp,int width, int height); +int sonix_decompress(unsigned char *outp, unsigned char *inp, int width, int height); void bayer2rgb24(unsigned char *dst, unsigned char *src, long int width, long int height); int vid_do_autobright(struct context *cnt, struct video_dev *viddev); #ifdef MJPEGT @@ -91,20 +92,19 @@ void mjpegtoyuv420p(unsigned char *map, unsigned char *cap_map, int width, int h int vid_startpipe(const char *dev_name, int width, int height, int); int vid_putpipe(int dev, unsigned char *image, int); unsigned char *v4l_start(struct video_dev *viddev, int width, int height, - int input, int norm, unsigned long freq, int tuner_number); + int input, int norm, unsigned long freq, int tuner_number); void v4l_set_input(struct context *cnt, struct video_dev *viddev, unsigned char *map, int width, int height, int input, - int norm, int skip, unsigned long freq, int tuner_number); + int norm, int skip, unsigned long freq, int tuner_number); int v4l_next(struct video_dev *viddev, unsigned char *map, int width, int height); /* video2.c */ unsigned char *v4l2_start(struct context *cnt, struct video_dev *viddev, int width, int height, - int input, int norm, unsigned long freq, int tuner_number); + int input, int norm, unsigned long freq, int tuner_number); void v4l2_set_input(struct context *cnt, struct video_dev *viddev, unsigned char *map, int width, int height, - struct config *conf); + struct config *conf); int v4l2_next(struct context *cnt, struct video_dev *viddev, unsigned char *map, int width, int height); void v4l2_close(struct video_dev *viddev); void v4l2_cleanup(struct video_dev *viddev); - #endif #endif /* _INCLUDE_VIDEO_H */ diff --git a/video2.c b/video2.c index 69bba3f..0d09115 100644 --- a/video2.c +++ b/video2.c @@ -1,22 +1,22 @@ /* - * video2.c + * video2.c * - * V4L2 interface with basically JPEG decompression support and even more ... - * Copyright 2006 Krzysztof Blaszkowski (kb@sysmikro.com.pl) - * 2007 Angel Carpintero (ack@telefonica.net) + * V4L2 interface with basically JPEG decompression support and even more ... + * Copyright 2006 Krzysztof Blaszkowski (kb@sysmikro.com.pl) + * 2007 Angel Carpintero (ack@telefonica.net) * Supported features and TODO * - preferred palette is JPEG which seems to be very popular for many 640x480 usb cams * - other supported palettes (NOT TESTED) - * V4L2_PIX_FMT_SBGGR8 ( sonix ) - * V4L2_PIX_FMT_SN9C10X ( sonix ) - * V4L2_PIX_FMT_MJPEG, ( tested ) - * V4L2_PIX_FMT_JPEG, ( tested ) - V4L2_PIX_FMT_RGB24, - V4L2_PIX_FMT_UYVY, ( tested ) - V4L2_PIX_FMT_YUV422P, - V4L2_PIX_FMT_YUV420, ( tested ) - V4L2_PIX_FMT_YUYV ( tested ) + * V4L2_PIX_FMT_SBGGR8 (sonix) + * V4L2_PIX_FMT_SN9C10X (sonix) + * V4L2_PIX_FMT_MJPEG, (tested) + * V4L2_PIX_FMT_JPEG, (tested) + V4L2_PIX_FMT_RGB24, + V4L2_PIX_FMT_UYVY, (tested) + V4L2_PIX_FMT_YUV422P, + V4L2_PIX_FMT_YUV420, (tested) + V4L2_PIX_FMT_YUYV (tested) * - setting tuner - NOT TESTED * - access to V4L2 device controls is missing. Partially added but requires some improvements likely. @@ -49,19 +49,6 @@ #ifndef WITHOUT_V4L #ifdef MOTION_V4L2 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include "motion.h" #include "netcam.h" #include "video.h" @@ -82,830 +69,826 @@ #ifndef V4L2_PIX_FMT_SBGGR8 /* see http://www.siliconimaging.com/RGB%20Bayer.htm */ -#define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B','A','8','1') /* 8 BGBG.. GRGR.. */ +#define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B','A','8','1') /* 8 BGBG.. GRGR.. */ #endif #ifndef V4L2_PIX_FMT_MJPEG -#define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M','J','P','G') /* Motion-JPEG */ +#define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M','J','P','G') /* Motion-JPEG */ #endif #ifndef V4L2_PIX_FMT_SN9C10X -#define V4L2_PIX_FMT_SN9C10X v4l2_fourcc('S','9','1','0') /* SN9C10x compression */ +#define V4L2_PIX_FMT_SN9C10X v4l2_fourcc('S','9','1','0') /* SN9C10x compression */ #endif -#define ZC301_V4L2_CID_DAC_MAGN V4L2_CID_PRIVATE_BASE -#define ZC301_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE+1) +#define ZC301_V4L2_CID_DAC_MAGN V4L2_CID_PRIVATE_BASE +#define ZC301_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE+1) static const u32 queried_ctrls[] = { - V4L2_CID_BRIGHTNESS, - V4L2_CID_CONTRAST, - V4L2_CID_SATURATION, - V4L2_CID_HUE, - - V4L2_CID_RED_BALANCE, - V4L2_CID_BLUE_BALANCE, - V4L2_CID_GAMMA, - V4L2_CID_EXPOSURE, - V4L2_CID_AUTOGAIN, - V4L2_CID_GAIN, - - ZC301_V4L2_CID_DAC_MAGN, - ZC301_V4L2_CID_GREEN_BALANCE, - 0 + V4L2_CID_BRIGHTNESS, + V4L2_CID_CONTRAST, + V4L2_CID_SATURATION, + V4L2_CID_HUE, + + V4L2_CID_RED_BALANCE, + V4L2_CID_BLUE_BALANCE, + V4L2_CID_GAMMA, + V4L2_CID_EXPOSURE, + V4L2_CID_AUTOGAIN, + V4L2_CID_GAIN, + + ZC301_V4L2_CID_DAC_MAGN, + ZC301_V4L2_CID_GREEN_BALANCE, + 0 }; typedef struct { + int fd; + char map; + u32 fps; - int fd; - char map; - u32 fps; - - struct v4l2_capability cap; - struct v4l2_format fmt; - struct v4l2_requestbuffers req; - struct v4l2_buffer buf; + struct v4l2_capability cap; + struct v4l2_format fmt; + struct v4l2_requestbuffers req; + struct v4l2_buffer buf; - netcam_buff *buffers; + netcam_buff *buffers; - s32 pframe; + s32 pframe; - u32 ctrl_flags; - struct v4l2_queryctrl *controls; + u32 ctrl_flags; + struct v4l2_queryctrl *controls; } src_v4l2_t; static int xioctl(int fd, int request, void *arg) { - int r; + int r; - do - r = ioctl(fd, request, arg); - while (-1 == r && EINTR == errno); + do + r = ioctl(fd, request, arg); + while (-1 == r && EINTR == errno); - return r; + return r; } static int v4l2_get_capability(src_v4l2_t * s) { - - if (xioctl(s->fd, VIDIOC_QUERYCAP, &s->cap) < 0) { - motion_log(LOG_ERR, 0, "%s: Not a V4L2 device?", __FUNCTION__); - return (-1); - } - - motion_log(LOG_INFO, 0, "%s: \n------------------------\ncap.driver: \"%s\"\n" - "cap.card: \"%s\"\n" - "cap.bus_info: \"%s\"\n" - "cap.capabilities=0x%08X\n------------------------", __FUNCTION__, - s->cap.driver, s->cap.card, s->cap.bus_info, s->cap.capabilities); - - if (s->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) - motion_log(LOG_INFO, 0, "- VIDEO_CAPTURE"); - if (s->cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) - motion_log(LOG_INFO, 0, "- VIDEO_OUTPUT"); - if (s->cap.capabilities & V4L2_CAP_VIDEO_OVERLAY) - motion_log(LOG_INFO, 0, "- VIDEO_OVERLAY"); - if (s->cap.capabilities & V4L2_CAP_VBI_CAPTURE) - motion_log(LOG_INFO, 0, "- VBI_CAPTURE"); - if (s->cap.capabilities & V4L2_CAP_VBI_OUTPUT) - motion_log(LOG_INFO, 0, "- VBI_OUTPUT"); - if (s->cap.capabilities & V4L2_CAP_RDS_CAPTURE) - motion_log(LOG_INFO, 0, "- RDS_CAPTURE"); - if (s->cap.capabilities & V4L2_CAP_TUNER) - motion_log(LOG_INFO, 0, "- TUNER"); - if (s->cap.capabilities & V4L2_CAP_AUDIO) - motion_log(LOG_INFO, 0, "- AUDIO"); - if (s->cap.capabilities & V4L2_CAP_READWRITE) - motion_log(LOG_INFO, 0, "- READWRITE"); - if (s->cap.capabilities & V4L2_CAP_ASYNCIO) - motion_log(LOG_INFO, 0, "- ASYNCIO"); - if (s->cap.capabilities & V4L2_CAP_STREAMING) - motion_log(LOG_INFO, 0, "- STREAMING"); - if (s->cap.capabilities & V4L2_CAP_TIMEPERFRAME) - motion_log(LOG_INFO, 0, "- TIMEPERFRAME"); - - if (!s->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) { - motion_log(LOG_ERR, 0, "%s: Device does not support capturing.", __FUNCTION__); - return (-1); - } - - return (0); + if (xioctl(s->fd, VIDIOC_QUERYCAP, &s->cap) < 0) { + motion_log(LOG_ERR, 0, "%s: Not a V4L2 device?", __FUNCTION__); + return (-1); + } + + motion_log(LOG_INFO, 0, "%s: \n------------------------\ncap.driver: \"%s\"\n" + "cap.card: \"%s\"\n" + "cap.bus_info: \"%s\"\n" + "cap.capabilities=0x%08X\n------------------------", __FUNCTION__, + s->cap.driver, s->cap.card, s->cap.bus_info, s->cap.capabilities); + + if (s->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) + motion_log(LOG_INFO, 0, "- VIDEO_CAPTURE"); + if (s->cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) + motion_log(LOG_INFO, 0, "- VIDEO_OUTPUT"); + if (s->cap.capabilities & V4L2_CAP_VIDEO_OVERLAY) + motion_log(LOG_INFO, 0, "- VIDEO_OVERLAY"); + if (s->cap.capabilities & V4L2_CAP_VBI_CAPTURE) + motion_log(LOG_INFO, 0, "- VBI_CAPTURE"); + if (s->cap.capabilities & V4L2_CAP_VBI_OUTPUT) + motion_log(LOG_INFO, 0, "- VBI_OUTPUT"); + if (s->cap.capabilities & V4L2_CAP_RDS_CAPTURE) + motion_log(LOG_INFO, 0, "- RDS_CAPTURE"); + if (s->cap.capabilities & V4L2_CAP_TUNER) + motion_log(LOG_INFO, 0, "- TUNER"); + if (s->cap.capabilities & V4L2_CAP_AUDIO) + motion_log(LOG_INFO, 0, "- AUDIO"); + if (s->cap.capabilities & V4L2_CAP_READWRITE) + motion_log(LOG_INFO, 0, "- READWRITE"); + if (s->cap.capabilities & V4L2_CAP_ASYNCIO) + motion_log(LOG_INFO, 0, "- ASYNCIO"); + if (s->cap.capabilities & V4L2_CAP_STREAMING) + motion_log(LOG_INFO, 0, "- STREAMING"); + if (s->cap.capabilities & V4L2_CAP_TIMEPERFRAME) + motion_log(LOG_INFO, 0, "- TIMEPERFRAME"); + + if (!s->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) { + motion_log(LOG_ERR, 0, "%s: Device does not support capturing.", __FUNCTION__); + return (-1); + } + + return 0; } static int v4l2_select_input(src_v4l2_t * s, int in, int norm, unsigned long freq_, int tuner_number ATTRIBUTE_UNUSED) { - struct v4l2_input input; - struct v4l2_standard standard; - v4l2_std_id std_id; - - if (in == 8) - in = 0; - - /* Set the input. */ - memset (&input, 0, sizeof (input)); - input.index = in; - if (xioctl(s->fd, VIDIOC_ENUMINPUT, &input) == -1) { - motion_log(LOG_ERR, 0, "%s: Unable to query input %d.\nVIDIOC_ENUMINPUT: %s", - __FUNCTION__, in, strerror(errno)); - return (-1); - } - - if (debug_level >= CAMERA_VIDEO) - motion_log(LOG_INFO, 0, "%s: name = \"%s\", type 0x%08X, status %08x", __FUNCTION__, input.name, - input.type, input.status); - - if ((input.type & V4L2_INPUT_TYPE_TUNER) && (debug_level >= CAMERA_VIDEO)) - motion_log(LOG_INFO, 0, "- TUNER"); - - if ((input.type & V4L2_INPUT_TYPE_CAMERA) && (debug_level >= CAMERA_VIDEO)) - motion_log(LOG_INFO, 0, "- CAMERA"); - - if (xioctl(s->fd, VIDIOC_S_INPUT, &in) == -1) { - motion_log(LOG_ERR, 0, "%s: Error selecting input %d\nVIDIOC_S_INPUT: %s", - __FUNCTION__, in, strerror(errno)); - return (-1); - } - - /* Set video standard usually webcams doesn't support the ioctl or return V4L2_STD_UNKNOWN */ - if (xioctl(s->fd, VIDIOC_G_STD, &std_id) == -1) { - if (debug_level >= CAMERA_VIDEO) - motion_log(LOG_INFO, 0, "%s: Device doesn't support VIDIOC_G_STD", __FUNCTION__); - std_id = 0; // V4L2_STD_UNKNOWN = 0 - } - - if (std_id) { - memset(&standard, 0, sizeof(standard)); - standard.index = 0; - - while (xioctl(s->fd, VIDIOC_ENUMSTD, &standard) == 0) { - if ((standard.id & std_id) && (debug_level >= CAMERA_VIDEO)) - motion_log(LOG_INFO, 0, "- video standard %s", standard.name); - - standard.index++; - } - - switch (norm) { - case 1: - std_id = V4L2_STD_NTSC; - break; - case 2: - std_id = V4L2_STD_SECAM; - break; - default: - std_id = V4L2_STD_PAL; - } - - if (xioctl(s->fd, VIDIOC_S_STD, &std_id) == -1) - motion_log(LOG_ERR, 0, "%s: Error selecting standard method %d\nVIDIOC_S_STD: %s", - __FUNCTION__, std_id, strerror(errno)); - - } - - /* If this input is attached to a tuner, set the frequency. */ - if (input.type & V4L2_INPUT_TYPE_TUNER) { - struct v4l2_tuner tuner; - struct v4l2_frequency freq; - - /* Query the tuners capabilities. */ - - memset(&tuner, 0, sizeof(struct v4l2_tuner)); - tuner.index = input.tuner; - - if (xioctl(s->fd, VIDIOC_G_TUNER, &tuner) == -1) { - motion_log(LOG_ERR, 0, "%s: VIDIOC_G_TUNER: %s", __FUNCTION__,strerror(errno)); - return (0); - } - - /* Set the frequency. */ - memset(&freq, 0, sizeof(struct v4l2_frequency)); - freq.tuner = input.tuner; - freq.type = V4L2_TUNER_ANALOG_TV; - freq.frequency = (freq_ / 1000) * 16; - - if (xioctl(s->fd, VIDIOC_S_FREQUENCY, &freq) == -1) { - motion_log(LOG_ERR, 0, "%s: VIDIOC_S_FREQUENCY: %s", __FUNCTION__,strerror(errno)); - return (0); - } - } - - return (0); + struct v4l2_input input; + struct v4l2_standard standard; + v4l2_std_id std_id; + + if (in == 8) + in = 0; + + /* Set the input. */ + memset (&input, 0, sizeof (input)); + input.index = in; + + if (xioctl(s->fd, VIDIOC_ENUMINPUT, &input) == -1) { + motion_log(LOG_ERR, 1, "%s: Unable to query input %d.\nVIDIOC_ENUMINPUT: ", + __FUNCTION__, in); + return (-1); + } + + if (debug_level >= CAMERA_VIDEO) + motion_log(LOG_INFO, 0, "%s: name = \"%s\", type 0x%08X, status %08x", __FUNCTION__, + input.name, input.type, input.status); + + if ((input.type & V4L2_INPUT_TYPE_TUNER) && (debug_level >= CAMERA_VIDEO)) + motion_log(LOG_INFO, 0, "- TUNER"); + + if ((input.type & V4L2_INPUT_TYPE_CAMERA) && (debug_level >= CAMERA_VIDEO)) + motion_log(LOG_INFO, 0, "- CAMERA"); + + if (xioctl(s->fd, VIDIOC_S_INPUT, &in) == -1) { + motion_log(LOG_ERR, 1, "%s: Error selecting input %d\nVIDIOC_S_INPUT: ", + __FUNCTION__, in); + return (-1); + } + + /* Set video standard usually webcams doesn't support the ioctl or return V4L2_STD_UNKNOWN */ + if (xioctl(s->fd, VIDIOC_G_STD, &std_id) == -1) { + if (debug_level >= CAMERA_VIDEO) + motion_log(LOG_INFO, 0, "%s: Device doesn't support VIDIOC_G_STD", __FUNCTION__); + std_id = 0; // V4L2_STD_UNKNOWN = 0 + } + + if (std_id) { + memset(&standard, 0, sizeof(standard)); + standard.index = 0; + + while (xioctl(s->fd, VIDIOC_ENUMSTD, &standard) == 0) { + if ((standard.id & std_id) && (debug_level >= CAMERA_VIDEO)) + motion_log(LOG_INFO, 0, "- video standard %s", standard.name); + + standard.index++; + } + + switch (norm) { + case 1: + std_id = V4L2_STD_NTSC; + break; + case 2: + std_id = V4L2_STD_SECAM; + break; + default: + std_id = V4L2_STD_PAL; + } + + if (xioctl(s->fd, VIDIOC_S_STD, &std_id) == -1) + motion_log(LOG_ERR, 1, "%s: Error selecting standard method %d\nVIDIOC_S_STD: ", + __FUNCTION__, std_id); + + } + + /* If this input is attached to a tuner, set the frequency. */ + if (input.type & V4L2_INPUT_TYPE_TUNER) { + struct v4l2_tuner tuner; + struct v4l2_frequency freq; + + /* Query the tuners capabilities. */ + + memset(&tuner, 0, sizeof(struct v4l2_tuner)); + tuner.index = input.tuner; + + if (xioctl(s->fd, VIDIOC_G_TUNER, &tuner) == -1) { + motion_log(LOG_ERR, 1, "%s: VIDIOC_G_TUNER: ", __FUNCTION__); + return (0); + } + + /* Set the frequency. */ + memset(&freq, 0, sizeof(struct v4l2_frequency)); + freq.tuner = input.tuner; + freq.type = V4L2_TUNER_ANALOG_TV; + freq.frequency = (freq_ / 1000) * 16; + + if (xioctl(s->fd, VIDIOC_S_FREQUENCY, &freq) == -1) { + motion_log(LOG_ERR, 1, "%s: VIDIOC_S_FREQUENCY: ", __FUNCTION__); + return (0); + } + } + + return 0; } static int v4l2_set_pix_format(struct context *cnt, src_v4l2_t * s, int *width, int *height) { - struct v4l2_fmtdesc fmt; - short int v4l2_pal; - - static const u32 supported_formats[] = { /* higher index means better chance to be used */ - V4L2_PIX_FMT_SN9C10X, - V4L2_PIX_FMT_SBGGR8, - V4L2_PIX_FMT_MJPEG, - V4L2_PIX_FMT_JPEG, - V4L2_PIX_FMT_RGB24, - V4L2_PIX_FMT_UYVY, - V4L2_PIX_FMT_YUYV, - V4L2_PIX_FMT_YUV422P, - V4L2_PIX_FMT_YUV420, - 0 - }; - - short int index_format = -1; - - memset(&fmt, 0, sizeof(struct v4l2_fmtdesc)); - fmt.index = v4l2_pal = 0; - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - motion_log(LOG_INFO, 0, "%s: Supported palettes:", __FUNCTION__); - - while (xioctl(s->fd, VIDIOC_ENUM_FMT, &fmt) != -1) { - short int i; - - motion_log(LOG_INFO, 0, "%i: %c%c%c%c (%s)", v4l2_pal, fmt.pixelformat >> 0, - fmt.pixelformat >> 8, fmt.pixelformat >> 16, - fmt.pixelformat >> 24, fmt.description); - - for (i = 0; supported_formats[i]; i++) - if (supported_formats[i] == fmt.pixelformat && i > index_format) { - if (cnt->conf.v4l2_palette == i) { - index_format = cnt->conf.v4l2_palette; - motion_log(LOG_INFO, 0, "Selected palette %c%c%c%c", fmt.pixelformat >> 0, - fmt.pixelformat >> 8, fmt.pixelformat >> 16, - fmt.pixelformat >> 24); - i = 10; - break; - } - index_format = i; - } - - /* Chosen our selected palette, break from while */ - if (index_format == cnt->conf.v4l2_palette && index_format >= 0) - break; - - memset(&fmt, 0, sizeof(struct v4l2_fmtdesc)); - fmt.index = ++v4l2_pal; - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - } - - if (index_format >= 0) { - - u32 pixformat = supported_formats[index_format]; - - memset(&s->fmt, 0, sizeof(struct v4l2_format)); - s->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - s->fmt.fmt.pix.width = *width; - s->fmt.fmt.pix.height = *height; - s->fmt.fmt.pix.pixelformat = pixformat; - s->fmt.fmt.pix.field = V4L2_FIELD_ANY; - - if (xioctl(s->fd, VIDIOC_TRY_FMT, &s->fmt) != -1 && s->fmt.fmt.pix.pixelformat == pixformat) { - motion_log(LOG_INFO, 0, "%s: index_format %d Test palette %c%c%c%c (%dx%d)", - __FUNCTION__, index_format, pixformat >> 0, pixformat >> 8, - pixformat >> 16, pixformat >> 24, *width, *height); - - if (s->fmt.fmt.pix.width != (unsigned int) *width - || s->fmt.fmt.pix.height != (unsigned int) *height) { - - motion_log(LOG_INFO, 0, "%s: Adjusting resolution from %ix%i to %ix%i.", - __FUNCTION__, *width, *height, s->fmt.fmt.pix.width, s->fmt.fmt.pix.height); - - *width = s->fmt.fmt.pix.width; - *height = s->fmt.fmt.pix.height; - } - - if (xioctl(s->fd, VIDIOC_S_FMT, &s->fmt) == -1) { - motion_log(LOG_ERR, 0, "%s: Error setting pixel format.\nVIDIOC_S_FMT: %s", - __FUNCTION__, strerror(errno)); - return (-1); - } - - motion_log(LOG_INFO, 0, "%s: Using palette %c%c%c%c (%dx%d) bytesperlines %d sizeimage " - "%d colorspace %08x", __FUNCTION__, pixformat >> 0, pixformat >> 8, pixformat >> 16, - pixformat >> 24, *width, *height, s->fmt.fmt.pix.bytesperline, s->fmt.fmt.pix.sizeimage, - s->fmt.fmt. pix.colorspace); - - /* TODO: Review when it has been tested */ - if (pixformat == V4L2_PIX_FMT_MJPEG) { - struct v4l2_jpegcompression v4l2_jpeg; - - if (xioctl(s->fd, VIDIOC_G_JPEGCOMP, &v4l2_jpeg) == -1) { - motion_log(LOG_ERR, 0, "%s: VIDIOC_G_JPEGCOMP not supported but it should", - __FUNCTION__); - } else { - v4l2_jpeg.jpeg_markers |= V4L2_JPEG_MARKER_DHT; - if (xioctl(s->fd, VIDIOC_S_JPEGCOMP, &v4l2_jpeg) == -1) - motion_log(LOG_ERR, 0, "%s: VIDIOC_S_JPEGCOMP %s", - __FUNCTION__, strerror(errno)); - - } - } - return 0; - } - - motion_log(LOG_ERR, 0, "%s: VIDIOC_TRY_FMT failed for format %c%c%c%c (%s).", __FUNCTION__, - pixformat >> 0, pixformat >> 8, pixformat >> 16, pixformat >> 24, strerror(errno)); - - return -1; - } - - motion_log(LOG_ERR, 0, "%s: Unable to find a compatible palette format.", __FUNCTION__); - return (-1); + struct v4l2_fmtdesc fmt; + short int v4l2_pal; + + static const u32 supported_formats[] = { /* higher index means better chance to be used */ + V4L2_PIX_FMT_SN9C10X, + V4L2_PIX_FMT_SBGGR8, + V4L2_PIX_FMT_MJPEG, + V4L2_PIX_FMT_JPEG, + V4L2_PIX_FMT_RGB24, + V4L2_PIX_FMT_UYVY, + V4L2_PIX_FMT_YUYV, + V4L2_PIX_FMT_YUV422P, + V4L2_PIX_FMT_YUV420, + 0 + }; + + short int index_format = -1; + + memset(&fmt, 0, sizeof(struct v4l2_fmtdesc)); + fmt.index = v4l2_pal = 0; + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + motion_log(LOG_INFO, 0, "%s: Supported palettes:", __FUNCTION__); + + while (xioctl(s->fd, VIDIOC_ENUM_FMT, &fmt) != -1) { + short int i; + + motion_log(LOG_INFO, 0, "%i: %c%c%c%c (%s)", v4l2_pal, fmt.pixelformat >> 0, + fmt.pixelformat >> 8, fmt.pixelformat >> 16, fmt.pixelformat >> 24, fmt.description); + + for (i = 0; supported_formats[i]; i++) + if (supported_formats[i] == fmt.pixelformat && i > index_format) { + if (cnt->conf.v4l2_palette == i) { + index_format = cnt->conf.v4l2_palette; + motion_log(LOG_INFO, 0, "Selected palette %c%c%c%c", fmt.pixelformat >> 0, + fmt.pixelformat >> 8, fmt.pixelformat >> 16, fmt.pixelformat >> 24); + i = 10; + break; + } + index_format = i; + } + + /* Chosen our selected palette, break from while */ + if (index_format == cnt->conf.v4l2_palette && index_format >= 0) + break; + + memset(&fmt, 0, sizeof(struct v4l2_fmtdesc)); + fmt.index = ++v4l2_pal; + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + + if (index_format >= 0) { + + u32 pixformat = supported_formats[index_format]; + + memset(&s->fmt, 0, sizeof(struct v4l2_format)); + s->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + s->fmt.fmt.pix.width = *width; + s->fmt.fmt.pix.height = *height; + s->fmt.fmt.pix.pixelformat = pixformat; + s->fmt.fmt.pix.field = V4L2_FIELD_ANY; + + if (xioctl(s->fd, VIDIOC_TRY_FMT, &s->fmt) != -1 && s->fmt.fmt.pix.pixelformat == pixformat) { + motion_log(LOG_INFO, 0, "%s: index_format %d Test palette %c%c%c%c (%dx%d)", + __FUNCTION__, index_format, pixformat >> 0, pixformat >> 8, + pixformat >> 16, pixformat >> 24, *width, *height); + + if (s->fmt.fmt.pix.width != (unsigned int) *width + || s->fmt.fmt.pix.height != (unsigned int) *height) { + + motion_log(LOG_INFO, 0, "%s: Adjusting resolution from %ix%i to %ix%i.", + __FUNCTION__, *width, *height, s->fmt.fmt.pix.width, s->fmt.fmt.pix.height); + + *width = s->fmt.fmt.pix.width; + *height = s->fmt.fmt.pix.height; + } + + if (xioctl(s->fd, VIDIOC_S_FMT, &s->fmt) == -1) { + motion_log(LOG_ERR, 1, "%s: Error setting pixel format.\nVIDIOC_S_FMT: ", + __FUNCTION__); + return (-1); + } + + motion_log(LOG_INFO, 0, "%s: Using palette %c%c%c%c (%dx%d) bytesperlines %d sizeimage " + "%d colorspace %08x", __FUNCTION__, pixformat >> 0, pixformat >> 8, + pixformat >> 16, pixformat >> 24, *width, *height, s->fmt.fmt.pix.bytesperline, + s->fmt.fmt.pix.sizeimage, s->fmt.fmt.pix.colorspace); + + /* TODO: Review when it has been tested */ + if (pixformat == V4L2_PIX_FMT_MJPEG) { + struct v4l2_jpegcompression v4l2_jpeg; + + if (xioctl(s->fd, VIDIOC_G_JPEGCOMP, &v4l2_jpeg) == -1) { + motion_log(LOG_ERR, 1, "%s: VIDIOC_G_JPEGCOMP not supported but it should", + __FUNCTION__); + } else { + v4l2_jpeg.jpeg_markers |= V4L2_JPEG_MARKER_DHT; + if (xioctl(s->fd, VIDIOC_S_JPEGCOMP, &v4l2_jpeg) == -1) + motion_log(LOG_ERR, 1, "%s: VIDIOC_S_JPEGCOMP ", + __FUNCTION__); + } + } + return 0; + } + + motion_log(LOG_ERR, 1, "%s: VIDIOC_TRY_FMT failed for format %c%c%c%c ", __FUNCTION__, + pixformat >> 0, pixformat >> 8, pixformat >> 16, pixformat >> 24); + + return -1; + } + + motion_log(LOG_ERR, 0, "%s: Unable to find a compatible palette format.", __FUNCTION__); + return (-1); } #if 0 -static void v4l2_set_fps(src_v4l2_t * s){ - struct v4l2_streamparm* setfps; - - setfps = (struct v4l2_streamparm *) calloc(1, sizeof(struct v4l2_streamparm)); - memset(setfps, 0, sizeof(struct v4l2_streamparm)); - setfps->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - setfps->parm.capture.timeperframe.numerator = 1; - setfps->parm.capture.timeperframe.denominator = s->fps; - if (xioctl(s->fd, VIDIOC_S_PARM, setfps) == -1) { - motion_log(LOG_ERR, 0, "v4l2_set_fps VIDIOC_S_PARM %s", strerror(errno)); - } +static void v4l2_set_fps(src_v4l2_t * s) { + struct v4l2_streamparm* setfps; + + setfps = (struct v4l2_streamparm *) calloc(1, sizeof(struct v4l2_streamparm)); + memset(setfps, 0, sizeof(struct v4l2_streamparm)); + setfps->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + setfps->parm.capture.timeperframe.numerator = 1; + setfps->parm.capture.timeperframe.denominator = s->fps; + + if (xioctl(s->fd, VIDIOC_S_PARM, setfps) == -1) + motion_log(LOG_ERR, 1, "%s: v4l2_set_fps VIDIOC_S_PARM ", __FUNCTION__); + } #endif static int v4l2_set_mmap(src_v4l2_t * s) { - enum v4l2_buf_type type; - u32 b; + enum v4l2_buf_type type; + u32 b; - /* Does the device support streaming? */ - if (!s->cap.capabilities & V4L2_CAP_STREAMING) - return (-1); + /* Does the device support streaming? */ + if (!s->cap.capabilities & V4L2_CAP_STREAMING) + return (-1); - memset(&s->req, 0, sizeof(struct v4l2_requestbuffers)); + memset(&s->req, 0, sizeof(struct v4l2_requestbuffers)); - s->req.count = MMAP_BUFFERS; - s->req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - s->req.memory = V4L2_MEMORY_MMAP; + s->req.count = MMAP_BUFFERS; + s->req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + s->req.memory = V4L2_MEMORY_MMAP; - if (xioctl(s->fd, VIDIOC_REQBUFS, &s->req) == -1) { - motion_log(LOG_ERR, 0, "%s: Error requesting buffers for memory map.\nVIDIOC_REQBUFS: %s", - __FUNCTION__, strerror(errno)); - return (-1); - } + if (xioctl(s->fd, VIDIOC_REQBUFS, &s->req) == -1) { + motion_log(LOG_ERR, 1, "%s: Error requesting buffers for memory map.\nVIDIOC_REQBUFS: ", + __FUNCTION__); + return -1; + } - motion_log(LOG_DEBUG, 0, "%s: mmap information: frames=%d", __FUNCTION__, s->req.count); + motion_log(LOG_DEBUG, 0, "%s: mmap information: frames=%d", __FUNCTION__, s->req.count); - if (s->req.count < MIN_MMAP_BUFFERS) { - motion_log(LOG_ERR, 0, "%s: Insufficient buffer memory.", __FUNCTION__); - return (-1); - } + if (s->req.count < MIN_MMAP_BUFFERS) { + motion_log(LOG_ERR, 1, "%s: Insufficient buffer memory.", __FUNCTION__); + return -1; + } - s->buffers = calloc(s->req.count, sizeof(netcam_buff)); - if (!s->buffers) { - motion_log(LOG_ERR, 1, "%s: Out of memory.", __FUNCTION__); - return (-1); - } + s->buffers = calloc(s->req.count, sizeof(netcam_buff)); - for (b = 0; b < s->req.count; b++) { - struct v4l2_buffer buf; + if (!s->buffers) { + motion_log(LOG_ERR, 1, "%s: Out of memory.", __FUNCTION__); + return -1; + } - memset(&buf, 0, sizeof(struct v4l2_buffer)); + for (b = 0; b < s->req.count; b++) { + struct v4l2_buffer buf; - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = b; + memset(&buf, 0, sizeof(struct v4l2_buffer)); - if (xioctl(s->fd, VIDIOC_QUERYBUF, &buf) == -1) { - motion_log(LOG_ERR, 0, "%s: Error querying buffer %i\nVIDIOC_QUERYBUF: %s", - __FUNCTION__, b, strerror(errno)); - free(s->buffers); - return (-1); - } + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = b; - s->buffers[b].size = buf.length; - s->buffers[b].ptr = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, s->fd, buf.m.offset); + if (xioctl(s->fd, VIDIOC_QUERYBUF, &buf) == -1) { + motion_log(LOG_ERR, 1, "%s: Error querying buffer %i\nVIDIOC_QUERYBUF: ", + __FUNCTION__, b); + free(s->buffers); + return -1; + } - if (s->buffers[b].ptr == MAP_FAILED) { - motion_log(LOG_ERR, 0, "%s: Error mapping buffer %i\nmmap: %s", - __FUNCTION__, b, strerror(errno)); - free(s->buffers); - return (-1); - } + s->buffers[b].size = buf.length; + s->buffers[b].ptr = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, s->fd, buf.m.offset); - motion_log(LOG_DEBUG, 0, "%s: %i length=%d", __FUNCTION__, b, buf.length); - } + if (s->buffers[b].ptr == MAP_FAILED) { + motion_log(LOG_ERR, 1, "%s: Error mapping buffer %i\nmmap: ", + __FUNCTION__, b); + free(s->buffers); + return -1; + } - s->map = -1; + motion_log(LOG_DEBUG, 0, "%s: %i length=%d", __FUNCTION__, b, buf.length); + } - for (b = 0; b < s->req.count; b++) { - memset(&s->buf, 0, sizeof(struct v4l2_buffer)); + s->map = -1; - s->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - s->buf.memory = V4L2_MEMORY_MMAP; - s->buf.index = b; + for (b = 0; b < s->req.count; b++) { + memset(&s->buf, 0, sizeof(struct v4l2_buffer)); - if (xioctl(s->fd, VIDIOC_QBUF, &s->buf) == -1) { - motion_log(LOG_ERR, 0, "%s: VIDIOC_QBUF: %s", __FUNCTION__, strerror(errno)); - return (-1); - } - } + s->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + s->buf.memory = V4L2_MEMORY_MMAP; + s->buf.index = b; - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (xioctl(s->fd, VIDIOC_QBUF, &s->buf) == -1) { + motion_log(LOG_ERR, 1, "%s: VIDIOC_QBUF: ", __FUNCTION__); + return -1; + } + } - if (xioctl(s->fd, VIDIOC_STREAMON, &type) == -1) { - motion_log(LOG_ERR, 0, "%s: Error starting stream.\nVIDIOC_STREAMON: %s", - __FUNCTION__, strerror(errno)); - return (-1); - } + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - return (0); + if (xioctl(s->fd, VIDIOC_STREAMON, &type) == -1) { + motion_log(LOG_ERR, 1, "%s: Error starting stream.\nVIDIOC_STREAMON: ", + __FUNCTION__); + return -1; + } + + return 0; } static int v4l2_scan_controls(src_v4l2_t * s) { - int count, i; - struct v4l2_queryctrl queryctrl; + int count, i; + struct v4l2_queryctrl queryctrl; - memset(&queryctrl, 0, sizeof(struct v4l2_queryctrl)); + memset(&queryctrl, 0, sizeof(struct v4l2_queryctrl)); - for (i = 0, count = 0; queried_ctrls[i]; i++) { - queryctrl.id = queried_ctrls[i]; - if (xioctl(s->fd, VIDIOC_QUERYCTRL, &queryctrl)) - continue; + for (i = 0, count = 0; queried_ctrls[i]; i++) { + queryctrl.id = queried_ctrls[i]; + if (xioctl(s->fd, VIDIOC_QUERYCTRL, &queryctrl)) + continue; - count++; - s->ctrl_flags |= 1 << i; - } + count++; + s->ctrl_flags |= 1 << i; + } - if (count) { - struct v4l2_queryctrl *ctrl = s->controls = calloc(count, sizeof(struct v4l2_queryctrl)); + if (count) { + struct v4l2_queryctrl *ctrl = s->controls = calloc(count, sizeof(struct v4l2_queryctrl)); - if (!ctrl) { - motion_log(LOG_ERR, 0, "%s: Insufficient buffer memory.", __FUNCTION__); - return (-1); - } + if (!ctrl) { + motion_log(LOG_ERR, 1, "%s: Insufficient buffer memory.", __FUNCTION__); + return -1; + } - for (i = 0; queried_ctrls[i]; i++) { - if (s->ctrl_flags & (1 << i)) { - struct v4l2_control control; + for (i = 0; queried_ctrls[i]; i++) { + if (s->ctrl_flags & (1 << i)) { + struct v4l2_control control; - queryctrl.id = queried_ctrls[i]; - if (xioctl(s->fd, VIDIOC_QUERYCTRL, &queryctrl)) - continue; + queryctrl.id = queried_ctrls[i]; + if (xioctl(s->fd, VIDIOC_QUERYCTRL, &queryctrl)) + continue; - memcpy(ctrl, &queryctrl, sizeof(struct v4l2_queryctrl)); + memcpy(ctrl, &queryctrl, sizeof(struct v4l2_queryctrl)); - motion_log(LOG_INFO, 0, "%s: found control 0x%08x, \"%s\", range %d,%d %s", - __FUNCTION__,ctrl->id, ctrl->name, ctrl->minimum, ctrl->maximum, - ctrl->flags & V4L2_CTRL_FLAG_DISABLED ? "!DISABLED!" : ""); + motion_log(LOG_INFO, 0, "%s: found control 0x%08x, \"%s\", range %d,%d %s", + __FUNCTION__,ctrl->id, ctrl->name, ctrl->minimum, ctrl->maximum, + ctrl->flags & V4L2_CTRL_FLAG_DISABLED ? "!DISABLED!" : ""); - memset (&control, 0, sizeof (control)); - control.id = queried_ctrls[i]; - xioctl(s->fd, VIDIOC_G_CTRL, &control); - motion_log(LOG_INFO, 0, "%s: \t\"%s\", default %d, current %d", __FUNCTION__, - ctrl->name, ctrl->default_value, control.value); + memset (&control, 0, sizeof (control)); + control.id = queried_ctrls[i]; + xioctl(s->fd, VIDIOC_G_CTRL, &control); + motion_log(LOG_INFO, 0, "%s: \t\"%s\", default %d, current %d", __FUNCTION__, + ctrl->name, ctrl->default_value, control.value); - ctrl++; - } - } - } + ctrl++; + } + } + } - return 0; + return 0; } static int v4l2_set_control(src_v4l2_t * s, u32 cid, int value) { - int i, count; - - if (!s->controls) - return -1; - - for (i = 0, count = 0; queried_ctrls[i]; i++) { - if (s->ctrl_flags & (1 << i)) { - if (cid == queried_ctrls[i]) { - struct v4l2_queryctrl *ctrl = s->controls + count; - struct v4l2_control control; - int ret; - - memset (&control, 0, sizeof (control)); - control.id = queried_ctrls[i]; - - switch (ctrl->type) { - case V4L2_CTRL_TYPE_INTEGER: - value = control.value = - (value * (ctrl->maximum - ctrl->minimum) / 256) + ctrl->minimum; - ret = xioctl(s->fd, VIDIOC_S_CTRL, &control); - break; - - case V4L2_CTRL_TYPE_BOOLEAN: - value = control.value = value ? 1 : 0; - ret = xioctl(s->fd, VIDIOC_S_CTRL, &control); - break; - - default: - motion_log(LOG_ERR, 0, "%s: control type not supported yet", __FUNCTION__); - return -1; - } - if (debug_level >= CAMERA_VIDEO) - motion_log(LOG_INFO, 0, "%s: setting control \"%s\" to %d (ret %d %s) %s", - __FUNCTION__, ctrl->name, value, ret, ret ? strerror(errno) : "", - ctrl->flags & V4L2_CTRL_FLAG_DISABLED ? "Control is DISABLED!" : ""); - - return 0; - } - count++; - } - } - - return -1; + int i, count; + + if (!s->controls) + return -1; + + for (i = 0, count = 0; queried_ctrls[i]; i++) { + if (s->ctrl_flags & (1 << i)) { + if (cid == queried_ctrls[i]) { + struct v4l2_queryctrl *ctrl = s->controls + count; + struct v4l2_control control; + int ret; + + memset (&control, 0, sizeof (control)); + control.id = queried_ctrls[i]; + + switch (ctrl->type) { + case V4L2_CTRL_TYPE_INTEGER: + value = control.value = + (value * (ctrl->maximum - ctrl->minimum) / 256) + ctrl->minimum; + ret = xioctl(s->fd, VIDIOC_S_CTRL, &control); + break; + + case V4L2_CTRL_TYPE_BOOLEAN: + value = control.value = value ? 1 : 0; + ret = xioctl(s->fd, VIDIOC_S_CTRL, &control); + break; + + default: + motion_log(LOG_ERR, 0, "%s: control type not supported yet", __FUNCTION__); + return -1; + } + + if (debug_level >= CAMERA_VIDEO) + motion_log(LOG_INFO, 0, "%s: setting control \"%s\" to %d (ret %d %s) %s", + __FUNCTION__, ctrl->name, value, ret, ret ? strerror(errno) : "", + ctrl->flags & V4L2_CTRL_FLAG_DISABLED ? "Control is DISABLED!" : ""); + + return 0; + } + count++; + } + } + + return -1; } static void v4l2_picture_controls(struct context *cnt, struct video_dev *viddev) { - src_v4l2_t *s = (src_v4l2_t *) viddev->v4l2_private; - - if (cnt->conf.contrast && cnt->conf.contrast != viddev->contrast) { - viddev->contrast = cnt->conf.contrast; - v4l2_set_control(s, V4L2_CID_CONTRAST, viddev->contrast); - } - - if (cnt->conf.saturation && cnt->conf.saturation != viddev->saturation) { - viddev->saturation = cnt->conf.saturation; - v4l2_set_control(s, V4L2_CID_SATURATION, viddev->saturation); - } - - if (cnt->conf.hue && cnt->conf.hue != viddev->hue) { - viddev->hue = cnt->conf.hue; - v4l2_set_control(s, V4L2_CID_HUE, viddev->hue); - } - - if (cnt->conf.autobright) { - if (vid_do_autobright(cnt, viddev)) { - if (v4l2_set_control(s, V4L2_CID_BRIGHTNESS, viddev->brightness)) - v4l2_set_control(s, V4L2_CID_GAIN, viddev->brightness); - } - } else { - if (cnt->conf.brightness && cnt->conf.brightness != viddev->brightness) { - viddev->brightness = cnt->conf.brightness; - if (v4l2_set_control(s, V4L2_CID_BRIGHTNESS, viddev->brightness)) - v4l2_set_control(s, V4L2_CID_GAIN, viddev->brightness); - } - } + src_v4l2_t *s = (src_v4l2_t *) viddev->v4l2_private; + + if (cnt->conf.contrast && cnt->conf.contrast != viddev->contrast) { + viddev->contrast = cnt->conf.contrast; + v4l2_set_control(s, V4L2_CID_CONTRAST, viddev->contrast); + } + + if (cnt->conf.saturation && cnt->conf.saturation != viddev->saturation) { + viddev->saturation = cnt->conf.saturation; + v4l2_set_control(s, V4L2_CID_SATURATION, viddev->saturation); + } + + if (cnt->conf.hue && cnt->conf.hue != viddev->hue) { + viddev->hue = cnt->conf.hue; + v4l2_set_control(s, V4L2_CID_HUE, viddev->hue); + } + + if (cnt->conf.autobright) { + if (vid_do_autobright(cnt, viddev)) { + if (v4l2_set_control(s, V4L2_CID_BRIGHTNESS, viddev->brightness)) + v4l2_set_control(s, V4L2_CID_GAIN, viddev->brightness); + } + } else { + if (cnt->conf.brightness && cnt->conf.brightness != viddev->brightness) { + viddev->brightness = cnt->conf.brightness; + if (v4l2_set_control(s, V4L2_CID_BRIGHTNESS, viddev->brightness)) + v4l2_set_control(s, V4L2_CID_GAIN, viddev->brightness); + } + } } /* public functions */ unsigned char *v4l2_start(struct context *cnt, struct video_dev *viddev, int width, int height, - int input, int norm, unsigned long freq, int tuner_number) + int input, int norm, unsigned long freq, int tuner_number) { - src_v4l2_t *s; - - /* Allocate memory for the state structure. */ - if (!(s = calloc(sizeof(src_v4l2_t), 1))) { - motion_log(LOG_ERR, 0, "%s: Out of memory.", __FUNCTION__); - goto err; - } - - viddev->v4l2_private = s; - s->fd = viddev->fd; - s->fps = cnt->conf.frame_limit; - s->pframe = -1; - - if (v4l2_get_capability(s)) { - goto err; - } - - if (v4l2_select_input(s, input, norm, freq, tuner_number)) { - goto err; - } - - if (v4l2_set_pix_format(cnt, s, &width, &height)) { - goto err; - } - - if (v4l2_scan_controls(s)) { - goto err; - } + src_v4l2_t *s; + + /* Allocate memory for the state structure. */ + if (!(s = calloc(sizeof(src_v4l2_t), 1))) { + motion_log(LOG_ERR, 1, "%s: Out of memory.", __FUNCTION__); + goto err; + } + + viddev->v4l2_private = s; + s->fd = viddev->fd; + s->fps = cnt->conf.frame_limit; + s->pframe = -1; + + if (v4l2_get_capability(s)) + goto err; + + if (v4l2_select_input(s, input, norm, freq, tuner_number)) + goto err; + + if (v4l2_set_pix_format(cnt, s, &width, &height)) + goto err; + + if (v4l2_scan_controls(s)) + goto err; #if 0 - v4l2_set_fps(s); + v4l2_set_fps(s); #endif - if (v4l2_set_mmap(s)) { - goto err; - } - - viddev->size_map = 0; - viddev->v4l_buffers[0] = NULL; - viddev->v4l_maxbuffer = 1; - viddev->v4l_curbuffer = 0; - - viddev->v4l_fmt = VIDEO_PALETTE_YUV420P; - viddev->v4l_bufsize = (width * height * 3) / 2; - - - /* Update width and height with supported values from camera driver */ - viddev->width = width; - viddev->height = height; - - return (void *) 1; - - err: - if (s) - free(s); - viddev->v4l2_private = NULL; - viddev->v4l2 = 0; - return NULL; + if (v4l2_set_mmap(s)) + goto err; + + viddev->size_map = 0; + viddev->v4l_buffers[0] = NULL; + viddev->v4l_maxbuffer = 1; + viddev->v4l_curbuffer = 0; + + viddev->v4l_fmt = VIDEO_PALETTE_YUV420P; + viddev->v4l_bufsize = (width * height * 3) / 2; + + + /* Update width and height with supported values from camera driver */ + viddev->width = width; + viddev->height = height; + + return (void *) 1; + +err: + if (s) + free(s); + viddev->v4l2_private = NULL; + viddev->v4l2 = 0; + return NULL; } -void v4l2_set_input(struct context *cnt, struct video_dev *viddev, unsigned char *map, int width, int height, - struct config *conf) +void v4l2_set_input(struct context *cnt, struct video_dev *viddev, unsigned char *map, + int width, int height, struct config *conf) { - int i; - int input = conf->input; - int norm = conf->norm; - int skip = conf->roundrobin_skip; - unsigned long freq = conf->frequency; - int tuner_number = conf->tuner_number; - - if (input != viddev->input || width != viddev->width || height != viddev->height || - freq != viddev->freq || tuner_number != viddev->tuner_number) { - - struct timeval switchTime; - - v4l2_select_input((src_v4l2_t *) viddev->v4l2_private, input, norm, freq, tuner_number); - - gettimeofday(&switchTime, NULL); - - v4l2_picture_controls(cnt, viddev); - - viddev->input = input; - viddev->width = width; - viddev->height = height; - viddev->freq = freq; - viddev->tuner_number = tuner_number; - - - /* Skip all frames captured before switchtime, capture 1 after switchtime */ - { - src_v4l2_t *s = (src_v4l2_t *) viddev->v4l2_private; - unsigned int counter = 0; - if (debug_level >= CAMERA_VIDEO) - motion_log(LOG_DEBUG, 0, "%s: set_input_skip_frame switch_time=%ld:%ld", - __FUNCTION__, switchTime.tv_sec, switchTime.tv_usec); - - /* Avoid hang using the number of mmap buffers */ - while(counter < s->req.count) - { - counter++; - if (v4l2_next(cnt, viddev, map, width, height)) - break; - if (s->buf.timestamp.tv_sec > switchTime.tv_sec || - (s->buf.timestamp.tv_sec == switchTime.tv_sec && s->buf.timestamp.tv_usec > switchTime.tv_usec)) - break; - if (debug_level >= CAMERA_VIDEO) - motion_log(LOG_DEBUG, 0, "%s: got frame before switch timestamp=%ld:%ld", - __FUNCTION__, s->buf.timestamp.tv_sec, s->buf.timestamp.tv_usec); - } - } - - /* skip a few frames if needed */ - for (i = 1; i < skip; i++) - v4l2_next(cnt, viddev, map, width, height); - } else { - /* No round robin - we only adjust picture controls */ - v4l2_picture_controls(cnt, viddev); - } + int i; + int input = conf->input; + int norm = conf->norm; + int skip = conf->roundrobin_skip; + unsigned long freq = conf->frequency; + int tuner_number = conf->tuner_number; + + if (input != viddev->input || width != viddev->width || height != viddev->height || + freq != viddev->freq || tuner_number != viddev->tuner_number) { + + struct timeval switchTime; + + v4l2_select_input((src_v4l2_t *) viddev->v4l2_private, input, norm, freq, tuner_number); + + gettimeofday(&switchTime, NULL); + + v4l2_picture_controls(cnt, viddev); + + viddev->input = input; + viddev->width = width; + viddev->height = height; + viddev->freq = freq; + viddev->tuner_number = tuner_number; + + + /* Skip all frames captured before switchtime, capture 1 after switchtime */ + { + src_v4l2_t *s = (src_v4l2_t *) viddev->v4l2_private; + unsigned int counter = 0; + + if (debug_level >= CAMERA_VIDEO) + motion_log(LOG_DEBUG, 0, "%s: set_input_skip_frame switch_time=%ld:%ld", + __FUNCTION__, switchTime.tv_sec, switchTime.tv_usec); + + /* Avoid hang using the number of mmap buffers */ + while(counter < s->req.count) { + counter++; + if (v4l2_next(cnt, viddev, map, width, height)) + break; + + if (s->buf.timestamp.tv_sec > switchTime.tv_sec || + (s->buf.timestamp.tv_sec == switchTime.tv_sec && s->buf.timestamp.tv_usec > switchTime.tv_usec)) + break; + + if (debug_level >= CAMERA_VIDEO) + motion_log(LOG_DEBUG, 0, "%s: got frame before switch timestamp=%ld:%ld", + __FUNCTION__, s->buf.timestamp.tv_sec, s->buf.timestamp.tv_usec); + } + } + + /* skip a few frames if needed */ + for (i = 1; i < skip; i++) + v4l2_next(cnt, viddev, map, width, height); + } else { + /* No round robin - we only adjust picture controls */ + v4l2_picture_controls(cnt, viddev); + } } int v4l2_next(struct context *cnt, struct video_dev *viddev, unsigned char *map, int width, int height) { - sigset_t set, old; - src_v4l2_t *s = (src_v4l2_t *) viddev->v4l2_private; - - if (viddev->v4l_fmt != VIDEO_PALETTE_YUV420P) { - return V4L_FATAL_ERROR; - } - - /* Block signals during IOCTL */ - sigemptyset(&set); - sigaddset(&set, SIGCHLD); - sigaddset(&set, SIGALRM); - sigaddset(&set, SIGUSR1); - sigaddset(&set, SIGTERM); - sigaddset(&set, SIGHUP); - pthread_sigmask(SIG_BLOCK, &set, &old); - - if (s->pframe >= 0) { - if (xioctl(s->fd, VIDIOC_QBUF, &s->buf) == -1) { - motion_log(LOG_ERR, 0, "%s: VIDIOC_QBUF: %s", __FUNCTION__, strerror(errno)); - return (-1); - } - } - - memset(&s->buf, 0, sizeof(struct v4l2_buffer)); - - s->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - s->buf.memory = V4L2_MEMORY_MMAP; - - if (xioctl(s->fd, VIDIOC_DQBUF, &s->buf) == -1) { - - /* some drivers return EIO when there is no signal, - driver might dequeue an (empty) buffer despite - returning an error, or even stop capturing. - */ - if ( errno == EIO ) { - s->pframe++; - if ((u32)s->pframe >= s->req.count) s->pframe = 0; - s->buf.index = s->pframe; - - motion_log(LOG_ERR, 0, "%s: VIDIOC_DQBUF: EIO (s->pframe %d)", __FUNCTION__, s->pframe); - - return (1); - } - - motion_log(LOG_ERR, 0, "%s: VIDIOC_DQBUF: %s", __FUNCTION__, strerror(errno)); - - return (-1); - } - - s->pframe = s->buf.index; - s->buffers[s->buf.index].used = s->buf.bytesused; - s->buffers[s->buf.index].content_length = s->buf.bytesused; - - pthread_sigmask(SIG_UNBLOCK, &old, NULL); /*undo the signal blocking */ - - { - netcam_buff *the_buffer = &s->buffers[s->buf.index]; - - switch (s->fmt.fmt.pix.pixelformat) { - case V4L2_PIX_FMT_RGB24: - conv_rgb24toyuv420p(map, (unsigned char *) the_buffer->ptr, width, height); - return 0; - - case V4L2_PIX_FMT_UYVY: - conv_uyvyto420p(map, (unsigned char *) the_buffer->ptr, (unsigned)width, (unsigned)height); - return 0; - - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_YUV422P: - conv_yuv422to420p(map, (unsigned char *) the_buffer->ptr, width, height); - return 0; - - case V4L2_PIX_FMT_YUV420: - memcpy(map, the_buffer->ptr, viddev->v4l_bufsize); - return 0; - - case V4L2_PIX_FMT_MJPEG: + sigset_t set, old; + src_v4l2_t *s = (src_v4l2_t *) viddev->v4l2_private; + + if (viddev->v4l_fmt != VIDEO_PALETTE_YUV420P) + return V4L_FATAL_ERROR; + + /* Block signals during IOCTL */ + sigemptyset(&set); + sigaddset(&set, SIGCHLD); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGUSR1); + sigaddset(&set, SIGTERM); + sigaddset(&set, SIGHUP); + pthread_sigmask(SIG_BLOCK, &set, &old); + + if (s->pframe >= 0) { + if (xioctl(s->fd, VIDIOC_QBUF, &s->buf) == -1) { + motion_log(LOG_ERR, 1, "%s: VIDIOC_QBUF: ", __FUNCTION__); + return -1; + } + } + + memset(&s->buf, 0, sizeof(struct v4l2_buffer)); + + s->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + s->buf.memory = V4L2_MEMORY_MMAP; + + if (xioctl(s->fd, VIDIOC_DQBUF, &s->buf) == -1) { + + /* some drivers return EIO when there is no signal, + driver might dequeue an (empty) buffer despite + returning an error, or even stop capturing. + */ + if (errno == EIO) { + s->pframe++; + if ((u32)s->pframe >= s->req.count) s->pframe = 0; + s->buf.index = s->pframe; + + motion_log(LOG_ERR, 1, "%s: VIDIOC_DQBUF: EIO (s->pframe %d)", __FUNCTION__, s->pframe); + + return 1; + } + + motion_log(LOG_ERR, 1, "%s: VIDIOC_DQBUF: ", __FUNCTION__); + + return -1; + } + + s->pframe = s->buf.index; + s->buffers[s->buf.index].used = s->buf.bytesused; + s->buffers[s->buf.index].content_length = s->buf.bytesused; + + pthread_sigmask(SIG_UNBLOCK, &old, NULL); /*undo the signal blocking */ + + { + netcam_buff *the_buffer = &s->buffers[s->buf.index]; + + switch (s->fmt.fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + conv_rgb24toyuv420p(map, (unsigned char *) the_buffer->ptr, width, height); + return 0; + + case V4L2_PIX_FMT_UYVY: + conv_uyvyto420p(map, (unsigned char *) the_buffer->ptr, (unsigned)width, (unsigned)height); + return 0; + + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YUV422P: + conv_yuv422to420p(map, (unsigned char *) the_buffer->ptr, width, height); + return 0; + + case V4L2_PIX_FMT_YUV420: + memcpy(map, the_buffer->ptr, viddev->v4l_bufsize); + return 0; + + case V4L2_PIX_FMT_MJPEG: #ifdef MJPEGT - mjpegtoyuv420p(map, (unsigned char *) the_buffer->ptr, width, height, s->buffers[s->buf.index].content_length); - return 0; + mjpegtoyuv420p(map, (unsigned char *) the_buffer->ptr, width, height, s->buffers[s->buf.index].content_length); + return 0; #endif - case V4L2_PIX_FMT_JPEG: - return conv_jpeg2yuv420(cnt, map, the_buffer, width, height); - - case V4L2_PIX_FMT_SBGGR8: /* bayer */ - bayer2rgb24(cnt->imgs.common_buffer, (unsigned char *) the_buffer->ptr, width, height); - conv_rgb24toyuv420p(map, cnt->imgs.common_buffer, width, height); - return 0; - - case V4L2_PIX_FMT_SN9C10X: - sonix_decompress(map, (unsigned char *) the_buffer->ptr, width, height); - bayer2rgb24(cnt->imgs.common_buffer, map, width, height); - conv_rgb24toyuv420p(map, cnt->imgs.common_buffer, width, height); - return 0; - } - } - - return 1; + case V4L2_PIX_FMT_JPEG: + return conv_jpeg2yuv420(cnt, map, the_buffer, width, height); + + case V4L2_PIX_FMT_SBGGR8: /* bayer */ + bayer2rgb24(cnt->imgs.common_buffer, (unsigned char *) the_buffer->ptr, width, height); + conv_rgb24toyuv420p(map, cnt->imgs.common_buffer, width, height); + return 0; + + case V4L2_PIX_FMT_SN9C10X: + sonix_decompress(map, (unsigned char *) the_buffer->ptr, width, height); + bayer2rgb24(cnt->imgs.common_buffer, map, width, height); + conv_rgb24toyuv420p(map, cnt->imgs.common_buffer, width, height); + return 0; + } + } + + return 1; } void v4l2_close(struct video_dev *viddev) { - src_v4l2_t *s = (src_v4l2_t *) viddev->v4l2_private; - enum v4l2_buf_type type; + src_v4l2_t *s = (src_v4l2_t *) viddev->v4l2_private; + enum v4l2_buf_type type; - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - xioctl(s->fd, VIDIOC_STREAMOFF, &type); - close(s->fd); - s->fd = -1; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + xioctl(s->fd, VIDIOC_STREAMOFF, &type); + close(s->fd); + s->fd = -1; } void v4l2_cleanup(struct video_dev *viddev) { - src_v4l2_t *s = (src_v4l2_t *) viddev->v4l2_private; + src_v4l2_t *s = (src_v4l2_t *) viddev->v4l2_private; + + if (s->buffers) { + unsigned int i; - if (s->buffers) { - unsigned int i; + for (i = 0; i < s->req.count; i++) + munmap(s->buffers[i].ptr, s->buffers[i].size); - for (i = 0; i < s->req.count; i++) - munmap(s->buffers[i].ptr, s->buffers[i].size); - free(s->buffers); - s->buffers = NULL; - } + free(s->buffers); + s->buffers = NULL; + } - if (s->controls) { - free(s->controls); - s->controls = NULL; - } + if (s->controls) { + free(s->controls); + s->controls = NULL; + } - free(s); - viddev->v4l2_private = NULL; + free(s); + viddev->v4l2_private = NULL; } #endif diff --git a/video_common.c b/video_common.c index c3bf21b..3d182e3 100644 --- a/video_common.c +++ b/video_common.c @@ -2,15 +2,15 @@ * * Video stream functions for motion. * Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org) - * 2006 by Krzysztof Blaszkowski (kb@sysmikro.com.pl) - * 2007 by Angel Carpinteo (ack@telefonica.net) + * 2006 by Krzysztof Blaszkowski (kb@sysmikro.com.pl) + * 2007 by Angel Carpinteo (ack@telefonica.net) * This software is distributed under the GNU public license version 2 * See also the file 'COPYING'. * */ /* for rotation */ -#include "rotate.h" /* already includes motion.h */ +#include "rotate.h" /* already includes motion.h */ #include "video.h" #ifdef MJPEGT @@ -23,12 +23,12 @@ typedef unsigned char uint8_t; typedef unsigned short int uint16_t; typedef unsigned int uint32_t; -#define CLAMP(x) ((x)<0?0:((x)>255)?255:(x)) +#define CLAMP(x) ((x)<0?0:((x)>255)?255:(x)) typedef struct { - int is_abs; - int len; - int val; + int is_abs; + int len; + int val; } code_table_t; /* @@ -42,55 +42,55 @@ typedef struct { */ static void sonix_decompress_init(code_table_t * table) { - int i; - int is_abs, val, len; - - for (i = 0; i < 256; i++) { - is_abs = 0; - val = 0; - len = 0; - if ((i & 0x80) == 0) { - /* code 0 */ - val = 0; - len = 1; - } else if ((i & 0xE0) == 0x80) { - /* code 100 */ - val = +4; - len = 3; - } else if ((i & 0xE0) == 0xA0) { - /* code 101 */ - val = -4; - len = 3; - } else if ((i & 0xF0) == 0xD0) { - /* code 1101 */ - val = +11; - len = 4; - } else if ((i & 0xF0) == 0xF0) { - /* code 1111 */ - val = -11; - len = 4; - } else if ((i & 0xF8) == 0xC8) { - /* code 11001 */ - val = +20; - len = 5; - } else if ((i & 0xFC) == 0xC0) { - /* code 110000 */ - val = -20; - len = 6; - } else if ((i & 0xFC) == 0xC4) { - /* code 110001xx: unknown */ - val = 0; - len = 8; - } else if ((i & 0xF0) == 0xE0) { - /* code 1110xxxx */ - is_abs = 1; - val = (i & 0x0F) << 4; - len = 8; - } - table[i].is_abs = is_abs; - table[i].val = val; - table[i].len = len; - } + int i; + int is_abs, val, len; + + for (i = 0; i < 256; i++) { + is_abs = 0; + val = 0; + len = 0; + if ((i & 0x80) == 0) { + /* code 0 */ + val = 0; + len = 1; + } else if ((i & 0xE0) == 0x80) { + /* code 100 */ + val = +4; + len = 3; + } else if ((i & 0xE0) == 0xA0) { + /* code 101 */ + val = -4; + len = 3; + } else if ((i & 0xF0) == 0xD0) { + /* code 1101 */ + val = +11; + len = 4; + } else if ((i & 0xF0) == 0xF0) { + /* code 1111 */ + val = -11; + len = 4; + } else if ((i & 0xF8) == 0xC8) { + /* code 11001 */ + val = +20; + len = 5; + } else if ((i & 0xFC) == 0xC0) { + /* code 110000 */ + val = -20; + len = 6; + } else if ((i & 0xFC) == 0xC4) { + /* code 110001xx: unknown */ + val = 0; + len = 8; + } else if ((i & 0xF0) == 0xE0) { + /* code 1110xxxx */ + is_abs = 1; + val = (i & 0x0F) << 4; + len = 8; + } + table[i].is_abs = is_abs; + table[i].val = val; + table[i].len = len; + } } /* @@ -109,74 +109,74 @@ static void sonix_decompress_init(code_table_t * table) */ int sonix_decompress(unsigned char *outp, unsigned char *inp, int width, int height) { - int row, col; - int val; - int bitpos; - unsigned char code; - unsigned char *addr; - - /* local storage */ - static code_table_t table[256]; - static int init_done = 0; - - if (!init_done) { - init_done = 1; - sonix_decompress_init(table); - /* do sonix_decompress_init first! */ - //return -1; // so it has been done and now fall through - } - - bitpos = 0; - for (row = 0; row < height; row++) { - - col = 0; - - /* first two pixels in first two rows are stored as raw 8-bit */ - if (row < 2) { - addr = inp + (bitpos >> 3); - code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); - bitpos += 8; - *outp++ = code; - - addr = inp + (bitpos >> 3); - code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); - bitpos += 8; - *outp++ = code; - - col += 2; - } - - while (col < width) { - /* get bitcode from bitstream */ - addr = inp + (bitpos >> 3); - code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); - - /* update bit position */ - bitpos += table[code].len; - - /* calculate pixel value */ - val = table[code].val; - if (!table[code].is_abs) { - /* value is relative to top and left pixel */ - if (col < 2) { - /* left column: relative to top pixel */ - val += outp[-2 * width]; - } else if (row < 2) { - /* top row: relative to left pixel */ - val += outp[-2]; - } else { - /* main area: average of left pixel and top pixel */ - val += (outp[-2] + outp[-2 * width]) / 2; - } - } - - /* store pixel */ - *outp++ = CLAMP(val); - col++; - } - } - - return 0; + int row, col; + int val; + int bitpos; + unsigned char code; + unsigned char *addr; + + /* local storage */ + static code_table_t table[256]; + static int init_done = 0; + + if (!init_done) { + init_done = 1; + sonix_decompress_init(table); + /* do sonix_decompress_init first! */ + //return -1; // so it has been done and now fall through + } + + bitpos = 0; + for (row = 0; row < height; row++) { + + col = 0; + + /* first two pixels in first two rows are stored as raw 8-bit */ + if (row < 2) { + addr = inp + (bitpos >> 3); + code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); + bitpos += 8; + *outp++ = code; + + addr = inp + (bitpos >> 3); + code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); + bitpos += 8; + *outp++ = code; + + col += 2; + } + + while (col < width) { + /* get bitcode from bitstream */ + addr = inp + (bitpos >> 3); + code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); + + /* update bit position */ + bitpos += table[code].len; + + /* calculate pixel value */ + val = table[code].val; + if (!table[code].is_abs) { + /* value is relative to top and left pixel */ + if (col < 2) { + /* left column: relative to top pixel */ + val += outp[-2 * width]; + } else if (row < 2) { + /* top row: relative to left pixel */ + val += outp[-2]; + } else { + /* main area: average of left pixel and top pixel */ + val += (outp[-2] + outp[-2 * width]) / 2; + } + } + + /* store pixel */ + *outp++ = CLAMP(val); + col++; + } + } + + return 0; } /* @@ -189,242 +189,242 @@ int sonix_decompress(unsigned char *outp, unsigned char *inp, int width, int hei void bayer2rgb24(unsigned char *dst, unsigned char *src, long int width, long int height) { - long int i; - unsigned char *rawpt, *scanpt; - long int size; - - rawpt = src; - scanpt = dst; - size = width * height; - - for (i = 0; i < size; i++) { - if (((i / width) & 1) == 0) { // %2 changed to & 1 - if ((i & 1) == 0) { - /* B */ - if ((i > width) && ((i % width) > 0)) { - *scanpt++ = *rawpt; /* B */ - *scanpt++ = (*(rawpt - 1) + *(rawpt + 1) + *(rawpt + width) + *(rawpt - width)) / 4; /* G */ - *scanpt++ = (*(rawpt - width - 1) + *(rawpt - width + 1) + *(rawpt + width - 1) + *(rawpt + width + 1)) / 4; /* R */ - } else { - /* first line or left column */ - *scanpt++ = *rawpt; /* B */ - *scanpt++ = (*(rawpt + 1) + *(rawpt + width)) / 2; /* G */ - *scanpt++ = *(rawpt + width + 1); /* R */ - } - } else { - /* (B)G */ - if ((i > width) && ((i % width) < (width - 1))) { - *scanpt++ = (*(rawpt - 1) + *(rawpt + 1)) / 2; /* B */ - *scanpt++ = *rawpt; /* G */ - *scanpt++ = (*(rawpt + width) + *(rawpt - width)) / 2; /* R */ - } else { - /* first line or right column */ - *scanpt++ = *(rawpt - 1); /* B */ - *scanpt++ = *rawpt; /* G */ - *scanpt++ = *(rawpt + width); /* R */ - } - } - } else { - if ((i & 1) == 0) { - /* G(R) */ - if ((i < (width * (height - 1))) && ((i % width) > 0)) { - *scanpt++ = (*(rawpt + width) + *(rawpt - width)) / 2; /* B */ - *scanpt++ = *rawpt; /* G */ - *scanpt++ = (*(rawpt - 1) + *(rawpt + 1)) / 2; /* R */ - } else { - /* bottom line or left column */ - *scanpt++ = *(rawpt - width); /* B */ - *scanpt++ = *rawpt; /* G */ - *scanpt++ = *(rawpt + 1); /* R */ - } - } else { - /* R */ - if (i < (width * (height - 1)) && ((i % width) < (width - 1))) { - *scanpt++ = (*(rawpt - width - 1) + *(rawpt - width + 1) + *(rawpt + width - 1) + *(rawpt + width + 1)) / 4; /* B */ - *scanpt++ = (*(rawpt - 1) + *(rawpt + 1) + *(rawpt - width) + *(rawpt + width)) / 4; /* G */ - *scanpt++ = *rawpt; /* R */ - } else { - /* bottom line or right column */ - *scanpt++ = *(rawpt - width - 1); /* B */ - *scanpt++ = (*(rawpt - 1) + *(rawpt - width)) / 2; /* G */ - *scanpt++ = *rawpt; /* R */ - } - } - } - rawpt++; - } + long int i; + unsigned char *rawpt, *scanpt; + long int size; + + rawpt = src; + scanpt = dst; + size = width * height; + + for (i = 0; i < size; i++) { + if (((i / width) & 1) == 0) { // %2 changed to & 1 + if ((i & 1) == 0) { + /* B */ + if ((i > width) && ((i % width) > 0)) { + *scanpt++ = *rawpt; /* B */ + *scanpt++ = (*(rawpt - 1) + *(rawpt + 1) + *(rawpt + width) + *(rawpt - width)) / 4; /* G */ + *scanpt++ = (*(rawpt - width - 1) + *(rawpt - width + 1) + *(rawpt + width - 1) + *(rawpt + width + 1)) / 4; /* R */ + } else { + /* first line or left column */ + *scanpt++ = *rawpt; /* B */ + *scanpt++ = (*(rawpt + 1) + *(rawpt + width)) / 2; /* G */ + *scanpt++ = *(rawpt + width + 1); /* R */ + } + } else { + /* (B)G */ + if ((i > width) && ((i % width) < (width - 1))) { + *scanpt++ = (*(rawpt - 1) + *(rawpt + 1)) / 2; /* B */ + *scanpt++ = *rawpt; /* G */ + *scanpt++ = (*(rawpt + width) + *(rawpt - width)) / 2; /* R */ + } else { + /* first line or right column */ + *scanpt++ = *(rawpt - 1); /* B */ + *scanpt++ = *rawpt; /* G */ + *scanpt++ = *(rawpt + width); /* R */ + } + } + } else { + if ((i & 1) == 0) { + /* G(R) */ + if ((i < (width * (height - 1))) && ((i % width) > 0)) { + *scanpt++ = (*(rawpt + width) + *(rawpt - width)) / 2; /* B */ + *scanpt++ = *rawpt; /* G */ + *scanpt++ = (*(rawpt - 1) + *(rawpt + 1)) / 2; /* R */ + } else { + /* bottom line or left column */ + *scanpt++ = *(rawpt - width); /* B */ + *scanpt++ = *rawpt; /* G */ + *scanpt++ = *(rawpt + 1); /* R */ + } + } else { + /* R */ + if (i < (width * (height - 1)) && ((i % width) < (width - 1))) { + *scanpt++ = (*(rawpt - width - 1) + *(rawpt - width + 1) + *(rawpt + width - 1) + *(rawpt + width + 1)) / 4; /* B */ + *scanpt++ = (*(rawpt - 1) + *(rawpt + 1) + *(rawpt - width) + *(rawpt + width)) / 4; /* G */ + *scanpt++ = *rawpt; /* R */ + } else { + /* bottom line or right column */ + *scanpt++ = *(rawpt - width - 1); /* B */ + *scanpt++ = (*(rawpt - 1) + *(rawpt - width)) / 2; /* G */ + *scanpt++ = *rawpt; /* R */ + } + } + } + rawpt++; + } } void conv_yuv422to420p(unsigned char *map, unsigned char *cap_map, int width, int height) { - unsigned char *src, *dest, *src2, *dest2; - int i, j; - - /* Create the Y plane */ - src = cap_map; - dest = map; - for (i = width * height; i > 0; i--) { - *dest++ = *src; - src += 2; - } - /* Create U and V planes */ - src = cap_map + 1; - src2 = cap_map + width * 2 + 1; - dest = map + width * height; - dest2 = dest + (width * height) / 4; - for (i = height / 2; i > 0; i--) { - for (j = width / 2; j > 0; j--) { - *dest = ((int) *src + (int) *src2) / 2; - src += 2; - src2 += 2; - dest++; - *dest2 = ((int) *src + (int) *src2) / 2; - src += 2; - src2 += 2; - dest2++; - } - src += width * 2; - src2 += width * 2; - } + unsigned char *src, *dest, *src2, *dest2; + int i, j; + + /* Create the Y plane */ + src = cap_map; + dest = map; + for (i = width * height; i > 0; i--) { + *dest++ = *src; + src += 2; + } + /* Create U and V planes */ + src = cap_map + 1; + src2 = cap_map + width * 2 + 1; + dest = map + width * height; + dest2 = dest + (width * height) / 4; + for (i = height / 2; i > 0; i--) { + for (j = width / 2; j > 0; j--) { + *dest = ((int) *src + (int) *src2) / 2; + src += 2; + src2 += 2; + dest++; + *dest2 = ((int) *src + (int) *src2) / 2; + src += 2; + src2 += 2; + dest2++; + } + src += width * 2; + src2 += width * 2; + } } void conv_uyvyto420p(unsigned char *map, unsigned char *cap_map, unsigned int width, unsigned int height) { - uint8_t *pY = map; - uint8_t *pU = pY + (width * height); - uint8_t *pV = pU + (width * height) / 4; - uint32_t uv_offset = width * 4 * sizeof(uint8_t); - uint32_t ix, jx; - - for (ix = 0; ix < height; ix++) { - for (jx = 0; jx < width; jx += 2) { - uint16_t calc; - if ((ix&1) == 0) { - calc = *cap_map; - calc += *(cap_map + uv_offset); - calc /= 2; - *pU++ = (uint8_t) calc; - } - cap_map++; - *pY++ = *cap_map++; - if ((ix&1) == 0) { - calc = *cap_map; - calc += *(cap_map + uv_offset); - calc /= 2; - *pV++ = (uint8_t) calc; - } - cap_map++; - *pY++ = *cap_map++; - } - } + uint8_t *pY = map; + uint8_t *pU = pY + (width * height); + uint8_t *pV = pU + (width * height) / 4; + uint32_t uv_offset = width * 4 * sizeof(uint8_t); + uint32_t ix, jx; + + for (ix = 0; ix < height; ix++) { + for (jx = 0; jx < width; jx += 2) { + uint16_t calc; + + if ((ix&1) == 0) { + calc = *cap_map; + calc += *(cap_map + uv_offset); + calc /= 2; + *pU++ = (uint8_t) calc; + } + + cap_map++; + *pY++ = *cap_map++; + + if ((ix&1) == 0) { + calc = *cap_map; + calc += *(cap_map + uv_offset); + calc /= 2; + *pV++ = (uint8_t) calc; + } + + cap_map++; + *pY++ = *cap_map++; + } + } } void conv_rgb24toyuv420p(unsigned char *map, unsigned char *cap_map, int width, int height) { - unsigned char *y, *u, *v; - unsigned char *r, *g, *b; - int i, loop; - - b = cap_map; - g = b + 1; - r = g + 1; - y = map; - u = y + width * height; - v = u + (width * height) / 4; - memset(u, 0, width * height / 4); - memset(v, 0, width * height / 4); - - for (loop = 0; loop < height; loop++) { - for (i = 0; i < width; i += 2) { - *y++ = (9796 ** r + 19235 ** g + 3736 ** b) >> 15; - *u += ((-4784 ** r - 9437 ** g + 14221 ** b) >> 17) + 32; - *v += ((20218 ** r - 16941 ** g - 3277 ** b) >> 17) + 32; - r += 3; - g += 3; - b += 3; - *y++ = (9796 ** r + 19235 ** g + 3736 ** b) >> 15; - *u += ((-4784 ** r - 9437 ** g + 14221 ** b) >> 17) + 32; - *v += ((20218 ** r - 16941 ** g - 3277 ** b) >> 17) + 32; - r += 3; - g += 3; - b += 3; - u++; - v++; - } - - if ((loop & 1) == 0) { - u -= width / 2; - v -= width / 2; - } - } + unsigned char *y, *u, *v; + unsigned char *r, *g, *b; + int i, loop; + + b = cap_map; + g = b + 1; + r = g + 1; + y = map; + u = y + width * height; + v = u + (width * height) / 4; + memset(u, 0, width * height / 4); + memset(v, 0, width * height / 4); + + for (loop = 0; loop < height; loop++) { + for (i = 0; i < width; i += 2) { + *y++ = (9796 ** r + 19235 ** g + 3736 ** b) >> 15; + *u += ((-4784 ** r - 9437 ** g + 14221 ** b) >> 17) + 32; + *v += ((20218 ** r - 16941 ** g - 3277 ** b) >> 17) + 32; + r += 3; + g += 3; + b += 3; + *y++ = (9796 ** r + 19235 ** g + 3736 ** b) >> 15; + *u += ((-4784 ** r - 9437 ** g + 14221 ** b) >> 17) + 32; + *v += ((20218 ** r - 16941 ** g - 3277 ** b) >> 17) + 32; + r += 3; + g += 3; + b += 3; + u++; + v++; + } + + if ((loop & 1) == 0) { + u -= width / 2; + v -= width / 2; + } + } } int conv_jpeg2yuv420(struct context *cnt, unsigned char *dst, netcam_buff *buff, int width, int height) { - netcam_context netcam; + netcam_context netcam; - if (!buff || !dst) - return 3; + if (!buff || !dst) + return 3; - if (!buff->ptr) - return 2; /* Error decoding MJPEG frame */ + if (!buff->ptr) + return 2; /* Error decoding MJPEG frame */ - memset(&netcam, 0, sizeof(netcam)); - netcam.imgcnt_last = 1; - netcam.latest = buff; - netcam.width = width; - netcam.height = height; - netcam.cnt = cnt; + memset(&netcam, 0, sizeof(netcam)); + netcam.imgcnt_last = 1; + netcam.latest = buff; + netcam.width = width; + netcam.height = height; + netcam.cnt = cnt; - pthread_mutex_init(&netcam.mutex, NULL); - pthread_cond_init(&netcam.cap_cond, NULL); - pthread_cond_init(&netcam.pic_ready, NULL); - pthread_cond_init(&netcam.exiting, NULL); + pthread_mutex_init(&netcam.mutex, NULL); + pthread_cond_init(&netcam.cap_cond, NULL); + pthread_cond_init(&netcam.pic_ready, NULL); + pthread_cond_init(&netcam.exiting, NULL); - if (setjmp(netcam.setjmp_buffer)) { - return NETCAM_GENERAL_ERROR | NETCAM_JPEG_CONV_ERROR; - } + if (setjmp(netcam.setjmp_buffer)) + return NETCAM_GENERAL_ERROR | NETCAM_JPEG_CONV_ERROR; - return netcam_proc_jpeg(&netcam, dst); + return netcam_proc_jpeg(&netcam, dst); } #ifdef MJPEGT void mjpegtoyuv420p(unsigned char *map, unsigned char *cap_map, int width, int height, unsigned int size) { - uint8_t *yuv[3]; - unsigned char *y, *u, *v; - int loop; - - yuv[0] = mymalloc(width * height * sizeof(yuv[0][0])); - yuv[1] = mymalloc(width * height / 4 * sizeof(yuv[1][0])); - yuv[2] = mymalloc(width * height / 4 * sizeof(yuv[2][0])); - - - decode_jpeg_raw(cap_map, size, 0, 420, width, height, yuv[0], yuv[1], yuv[2]); - - y = map; - u = y + width * height; - v = u + (width * height) / 4; - memset(y, 0, width * height); - memset(u, 0, width * height / 4); - memset(v, 0, width * height / 4); - - for(loop = 0; loop < width * height; loop++) { - *map++ = yuv[0][loop]; - } - - for(loop = 0; loop < width * height / 4; loop++) { - *map++ = yuv[1][loop]; - } - - for(loop = 0; loop < width * height / 4; loop++) { - *map++ = yuv[2][loop]; - } - - free(yuv[0]); - free(yuv[1]); - free(yuv[2]); + uint8_t *yuv[3]; + unsigned char *y, *u, *v; + int loop; + + yuv[0] = mymalloc(width * height * sizeof(yuv[0][0])); + yuv[1] = mymalloc(width * height / 4 * sizeof(yuv[1][0])); + yuv[2] = mymalloc(width * height / 4 * sizeof(yuv[2][0])); + + + decode_jpeg_raw(cap_map, size, 0, 420, width, height, yuv[0], yuv[1], yuv[2]); + + y = map; + u = y + width * height; + v = u + (width * height) / 4; + memset(y, 0, width * height); + memset(u, 0, width * height / 4); + memset(v, 0, width * height / 4); + + for(loop = 0; loop < width * height; loop++) + *map++ = yuv[0][loop]; + + for(loop = 0; loop < width * height / 4; loop++) + *map++ = yuv[1][loop]; + + for(loop = 0; loop < width * height / 4; loop++) + *map++ = yuv[2][loop]; + + free(yuv[0]); + free(yuv[1]); + free(yuv[2]); } #endif @@ -454,49 +454,51 @@ void mjpegtoyuv420p(unsigned char *map, unsigned char *cap_map, int width, int h int vid_do_autobright(struct context *cnt, struct video_dev *viddev) { - int brightness_window_high; - int brightness_window_low; - int brightness_target; - int i, j = 0, avg = 0, step = 0; - unsigned char *image = cnt->imgs.image_virgin; /* Or cnt->current_image ? */ - - int make_change = 0; - - if (cnt->conf.brightness) - brightness_target = cnt->conf.brightness; - else - brightness_target = 128; - - brightness_window_high = MIN2(brightness_target + AUTOBRIGHT_HYSTERESIS, 255); - brightness_window_low = MAX2(brightness_target - AUTOBRIGHT_HYSTERESIS, 1); - - for (i = 0; i < cnt->imgs.motionsize; i += 101) { - avg += image[i]; - j++; - } - avg = avg / j; - - /* average is above window - turn down brightness - go for the target */ - if (avg > brightness_window_high) { - step = MIN2((avg - brightness_target) / AUTOBRIGHT_DAMPER + 1, viddev->brightness - AUTOBRIGHT_MIN); - if (viddev->brightness > step + 1 - AUTOBRIGHT_MIN) { - viddev->brightness -= step; - make_change = 1; - } - } else if (avg < brightness_window_low) { - /* average is below window - turn up brightness - go for the target */ - step = MIN2((brightness_target - avg) / AUTOBRIGHT_DAMPER + 1, AUTOBRIGHT_MAX - viddev->brightness); - if (viddev->brightness < AUTOBRIGHT_MAX - step) { - viddev->brightness += step; - make_change = 1; - } - } - - return make_change; + int brightness_window_high; + int brightness_window_low; + int brightness_target; + int i, j = 0, avg = 0, step = 0; + unsigned char *image = cnt->imgs.image_virgin; /* Or cnt->current_image ? */ + + int make_change = 0; + + if (cnt->conf.brightness) + brightness_target = cnt->conf.brightness; + else + brightness_target = 128; + + brightness_window_high = MIN2(brightness_target + AUTOBRIGHT_HYSTERESIS, 255); + brightness_window_low = MAX2(brightness_target - AUTOBRIGHT_HYSTERESIS, 1); + + for (i = 0; i < cnt->imgs.motionsize; i += 101) { + avg += image[i]; + j++; + } + avg = avg / j; + + /* average is above window - turn down brightness - go for the target */ + if (avg > brightness_window_high) { + step = MIN2((avg - brightness_target) / AUTOBRIGHT_DAMPER + 1, viddev->brightness - AUTOBRIGHT_MIN); + + if (viddev->brightness > step + 1 - AUTOBRIGHT_MIN) { + viddev->brightness -= step; + make_change = 1; + } + } else if (avg < brightness_window_low) { + /* average is below window - turn up brightness - go for the target */ + step = MIN2((brightness_target - avg) / AUTOBRIGHT_DAMPER + 1, AUTOBRIGHT_MAX - viddev->brightness); + + if (viddev->brightness < AUTOBRIGHT_MAX - step) { + viddev->brightness += step; + make_change = 1; + } + } + + return make_change; } /***************************************************************************** - Wrappers calling the actual capture routines + Wrappers calling the actual capture routines *****************************************************************************/ #ifndef WITHOUT_V4L @@ -523,7 +525,7 @@ static struct video_dev *viddevs = NULL; */ void vid_init(void) { - pthread_mutex_init(&vid_mutex, NULL); + pthread_mutex_init(&vid_mutex, NULL); } /** @@ -533,10 +535,10 @@ void vid_init(void) */ void vid_cleanup(void) { - pthread_mutex_destroy(&vid_mutex); + pthread_mutex_destroy(&vid_mutex); } -#endif /* WITHOUT_V4L */ +#endif /* WITHOUT_V4L */ /** * vid_close @@ -546,76 +548,76 @@ void vid_cleanup(void) void vid_close(struct context *cnt) { #ifndef WITHOUT_V4L - struct video_dev *dev = viddevs; - struct video_dev *prev = NULL; + struct video_dev *dev = viddevs; + struct video_dev *prev = NULL; #endif /* WITHOUT_V4L */ - /* Cleanup the netcam part */ - if(cnt->netcam) { - motion_log(LOG_DEBUG, 0, "%s: calling netcam_cleanup", __FUNCTION__); - netcam_cleanup(cnt->netcam, 0); - cnt->netcam = NULL; - return; - } + /* Cleanup the netcam part */ + if (cnt->netcam) { + motion_log(LOG_DEBUG, 0, "%s: calling netcam_cleanup", __FUNCTION__); + netcam_cleanup(cnt->netcam, 0); + cnt->netcam = NULL; + return; + } #ifndef WITHOUT_V4L - /* Cleanup the v4l part */ - pthread_mutex_lock(&vid_mutex); - while (dev) { - if (dev->fd == cnt->video_dev) - break; - prev = dev; - dev = dev->next; - } - pthread_mutex_unlock(&vid_mutex); - - /* Set it as closed in thread context */ - cnt->video_dev = -1; - - if (dev == NULL) { - motion_log(LOG_ERR, 0, "%s: Unable to find video device", __FUNCTION__); - return; - } - - if( --dev->usage_count == 0) { - motion_log(LOG_INFO, 0, "%s: Closing video device %s", __FUNCTION__, dev->video_device); + /* Cleanup the v4l part */ + pthread_mutex_lock(&vid_mutex); + while (dev) { + if (dev->fd == cnt->video_dev) + break; + prev = dev; + dev = dev->next; + } + pthread_mutex_unlock(&vid_mutex); + + /* Set it as closed in thread context */ + cnt->video_dev = -1; + + if (dev == NULL) { + motion_log(LOG_ERR, 0, "%s: Unable to find video device", __FUNCTION__); + return; + } + + if (--dev->usage_count == 0) { + motion_log(LOG_INFO, 0, "%s: Closing video device %s", __FUNCTION__, dev->video_device); #ifdef MOTION_V4L2 - if (dev->v4l2) { - v4l2_close(dev); - v4l2_cleanup(dev); - } else { + if (dev->v4l2) { + v4l2_close(dev); + v4l2_cleanup(dev); + } else { #endif - close(dev->fd); - munmap(viddevs->v4l_buffers[0], viddevs->size_map); - munmap(viddevs->v4l_buffers[1], viddevs->size_map); + close(dev->fd); + munmap(viddevs->v4l_buffers[0], viddevs->size_map); + munmap(viddevs->v4l_buffers[1], viddevs->size_map); #ifdef MOTION_V4L2 - } + } #endif - dev->fd = -1; - pthread_mutex_lock(&vid_mutex); - /* Remove from list */ - if (prev == NULL) - viddevs = dev->next; - else - prev->next = dev->next; - pthread_mutex_unlock(&vid_mutex); - - pthread_mutexattr_destroy(&dev->attr); - pthread_mutex_destroy(&dev->mutex); - free(dev); - } else { - motion_log(LOG_INFO, 0, "%s: Still %d users of video device %s, so we don't close it now", - __FUNCTION__, dev->usage_count, dev->video_device); - /* There is still at least one thread using this device - * If we own it, release it - */ - if (dev->owner == cnt->threadnr) { - dev->frames = 0; - dev->owner = -1; - pthread_mutex_unlock(&dev->mutex); - } - } + dev->fd = -1; + pthread_mutex_lock(&vid_mutex); + /* Remove from list */ + if (prev == NULL) + viddevs = dev->next; + else + prev->next = dev->next; + pthread_mutex_unlock(&vid_mutex); + + pthread_mutexattr_destroy(&dev->attr); + pthread_mutex_destroy(&dev->mutex); + free(dev); + } else { + motion_log(LOG_INFO, 0, "%s: Still %d users of video device %s, so we don't close it now", + __FUNCTION__, dev->usage_count, dev->video_device); + /* There is still at least one thread using this device + * If we own it, release it + */ + if (dev->owner == cnt->threadnr) { + dev->frames = 0; + dev->owner = -1; + pthread_mutex_unlock(&dev->mutex); + } + } #endif /* WITHOUT_V4L */ } @@ -646,175 +648,175 @@ void vid_close(struct context *cnt) */ static int vid_v4lx_start(struct context *cnt) { - struct config *conf = &cnt->conf; - int fd = -1; - struct video_dev *dev; - - int width, height, input, norm, tuner_number; - unsigned long frequency; - - /* We use width and height from conf in this function. They will be assigned - * to width and height in imgs here, and cap_width and cap_height in - * rotate_data won't be set until in rotate_init. - * Motion requires that width and height is a multiple of 16 so we check - * for this first. - */ - if (conf->width % 16) { - motion_log(LOG_ERR, 0, "%s: config image width (%d) is not modulo 16", - __FUNCTION__, conf->width); - return -1; - } - - if (conf->height % 16) { - motion_log(LOG_ERR, 0, "%s: config image height (%d) is not modulo 16", - __FUNCTION__, conf->height); - return -1; - } - - width = conf->width; - height = conf->height; - input = conf->input; - norm = conf->norm; - frequency = conf->frequency; - tuner_number = conf->tuner_number; - - pthread_mutex_lock(&vid_mutex); - - /* Transfer width and height from conf to imgs. The imgs values are the ones - * that is used internally in Motion. That way, setting width and height via - * http remote control won't screw things up. - */ - cnt->imgs.width = width; - cnt->imgs.height = height; - - /* First we walk through the already discovered video devices to see - * if we have already setup the same device before. If this is the case - * the device is a Round Robin device and we set the basic settings - * and return the file descriptor - */ - dev = viddevs; - while (dev) { - if (!strcmp(conf->video_device, dev->video_device)) { - dev->usage_count++; - cnt->imgs.type = dev->v4l_fmt; - switch (cnt->imgs.type) { - case VIDEO_PALETTE_GREY: - cnt->imgs.motionsize = width * height; - cnt->imgs.size = width * height; - break; - case VIDEO_PALETTE_YUYV: - case VIDEO_PALETTE_RGB24: - case VIDEO_PALETTE_YUV422: - cnt->imgs.type = VIDEO_PALETTE_YUV420P; - case VIDEO_PALETTE_YUV420P: - cnt->imgs.motionsize = width * height; - cnt->imgs.size = (width * height * 3) / 2; - break; - } - pthread_mutex_unlock(&vid_mutex); - return dev->fd; - } - dev = dev->next; - } - - dev = mymalloc(sizeof(struct video_dev)); - memset(dev, 0, sizeof(struct video_dev)); - - dev->video_device = conf->video_device; - - fd = open(dev->video_device, O_RDWR); - - if (fd < 0) { - motion_log(LOG_ERR, 1, "%s: Failed to open video device %s", - __FUNCTION__, conf->video_device); - free(dev); - pthread_mutex_unlock(&vid_mutex); - return -1; - } - - pthread_mutexattr_init(&dev->attr); - pthread_mutex_init(&dev->mutex, &dev->attr); - - dev->usage_count = 1; - dev->fd = fd; - dev->input = input; - dev->height = height; - dev->width = width; - dev->freq = frequency; - dev->tuner_number = tuner_number; - - /* We set brightness, contrast, saturation and hue = 0 so that they only get - * set if the config is not zero. - */ - dev->brightness = 0; - dev->contrast = 0; - dev->saturation = 0; - dev->hue = 0; - dev->owner = -1; - dev->v4l_fmt = VIDEO_PALETTE_YUV420P; + struct config *conf = &cnt->conf; + int fd = -1; + struct video_dev *dev; + + int width, height, input, norm, tuner_number; + unsigned long frequency; + + /* We use width and height from conf in this function. They will be assigned + * to width and height in imgs here, and cap_width and cap_height in + * rotate_data won't be set until in rotate_init. + * Motion requires that width and height is a multiple of 16 so we check + * for this first. + */ + if (conf->width % 16) { + motion_log(LOG_ERR, 0, "%s: config image width (%d) is not modulo 16", + __FUNCTION__, conf->width); + return -1; + } + + if (conf->height % 16) { + motion_log(LOG_ERR, 0, "%s: config image height (%d) is not modulo 16", + __FUNCTION__, conf->height); + return -1; + } + + width = conf->width; + height = conf->height; + input = conf->input; + norm = conf->norm; + frequency = conf->frequency; + tuner_number = conf->tuner_number; + + pthread_mutex_lock(&vid_mutex); + + /* Transfer width and height from conf to imgs. The imgs values are the ones + * that is used internally in Motion. That way, setting width and height via + * http remote control won't screw things up. + */ + cnt->imgs.width = width; + cnt->imgs.height = height; + + /* First we walk through the already discovered video devices to see + * if we have already setup the same device before. If this is the case + * the device is a Round Robin device and we set the basic settings + * and return the file descriptor + */ + dev = viddevs; + while (dev) { + if (!strcmp(conf->video_device, dev->video_device)) { + dev->usage_count++; + cnt->imgs.type = dev->v4l_fmt; + switch (cnt->imgs.type) { + case VIDEO_PALETTE_GREY: + cnt->imgs.motionsize = width * height; + cnt->imgs.size = width * height; + break; + case VIDEO_PALETTE_YUYV: + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_YUV422: + cnt->imgs.type = VIDEO_PALETTE_YUV420P; + case VIDEO_PALETTE_YUV420P: + cnt->imgs.motionsize = width * height; + cnt->imgs.size = (width * height * 3) / 2; + break; + } + pthread_mutex_unlock(&vid_mutex); + return dev->fd; + } + dev = dev->next; + } + + dev = mymalloc(sizeof(struct video_dev)); + memset(dev, 0, sizeof(struct video_dev)); + + dev->video_device = conf->video_device; + + fd = open(dev->video_device, O_RDWR); + + if (fd < 0) { + motion_log(LOG_ERR, 1, "%s: Failed to open video device %s", + __FUNCTION__, conf->video_device); + free(dev); + pthread_mutex_unlock(&vid_mutex); + return -1; + } + + pthread_mutexattr_init(&dev->attr); + pthread_mutex_init(&dev->mutex, &dev->attr); + + dev->usage_count = 1; + dev->fd = fd; + dev->input = input; + dev->height = height; + dev->width = width; + dev->freq = frequency; + dev->tuner_number = tuner_number; + + /* We set brightness, contrast, saturation and hue = 0 so that they only get + * set if the config is not zero. + */ + dev->brightness = 0; + dev->contrast = 0; + dev->saturation = 0; + dev->hue = 0; + dev->owner = -1; + dev->v4l_fmt = VIDEO_PALETTE_YUV420P; #ifdef MOTION_V4L2 - /* First lets try V4L2 and if it's not supported V4L1 */ + /* First lets try V4L2 and if it's not supported V4L1 */ - dev->v4l2 = 1; + dev->v4l2 = 1; - if (!v4l2_start(cnt, dev, width, height, input, norm, frequency, tuner_number)) { - /* restore width & height before test with v4l - * because could be changed in v4l2_start () - */ - dev->width = width; - dev->height = height; + if (!v4l2_start(cnt, dev, width, height, input, norm, frequency, tuner_number)) { + /* restore width & height before test with v4l + * because could be changed in v4l2_start () + */ + dev->width = width; + dev->height = height; #endif - if (!v4l_start(dev, width, height, input, norm, frequency, tuner_number)) { - close(dev->fd); - pthread_mutexattr_destroy(&dev->attr); - pthread_mutex_destroy(&dev->mutex); - free(dev); + if (!v4l_start(dev, width, height, input, norm, frequency, tuner_number)) { + close(dev->fd); + pthread_mutexattr_destroy(&dev->attr); + pthread_mutex_destroy(&dev->mutex); + free(dev); - pthread_mutex_unlock(&vid_mutex); - return -1; - } + pthread_mutex_unlock(&vid_mutex); + return -1; + } #ifdef MOTION_V4L2 - dev->v4l2 = 0; - } + dev->v4l2 = 0; + } #endif - if (dev->v4l2 == 0) { - motion_log(-1, 0, "%s: Using V4L1", __FUNCTION__); - } else { - motion_log(-1, 0, "%s: Using V4L2", __FUNCTION__); - /* Update width & height because could be changed in v4l2_start () */ - width = dev->width; - height = dev->height; - cnt->imgs.width = width; - cnt->imgs.height = height; - } - - cnt->imgs.type = dev->v4l_fmt; - - switch (cnt->imgs.type) { - case VIDEO_PALETTE_GREY: - cnt->imgs.size = width * height; - cnt->imgs.motionsize = width * height; - break; - case VIDEO_PALETTE_YUYV: - case VIDEO_PALETTE_RGB24: - case VIDEO_PALETTE_YUV422: - cnt->imgs.type = VIDEO_PALETTE_YUV420P; - case VIDEO_PALETTE_YUV420P: - cnt->imgs.size = (width * height * 3) / 2; - cnt->imgs.motionsize = width * height; - break; - } - - /* Insert into linked list */ - dev->next = viddevs; - viddevs = dev; - - pthread_mutex_unlock(&vid_mutex); - -return fd; + if (dev->v4l2 == 0) { + motion_log(-1, 0, "%s: Using V4L1", __FUNCTION__); + } else { + motion_log(-1, 0, "%s: Using V4L2", __FUNCTION__); + /* Update width & height because could be changed in v4l2_start () */ + width = dev->width; + height = dev->height; + cnt->imgs.width = width; + cnt->imgs.height = height; + } + + cnt->imgs.type = dev->v4l_fmt; + + switch (cnt->imgs.type) { + case VIDEO_PALETTE_GREY: + cnt->imgs.size = width * height; + cnt->imgs.motionsize = width * height; + break; + case VIDEO_PALETTE_YUYV: + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_YUV422: + cnt->imgs.type = VIDEO_PALETTE_YUV420P; + case VIDEO_PALETTE_YUV420P: + cnt->imgs.size = (width * height * 3) / 2; + cnt->imgs.motionsize = width * height; + break; + } + + /* Insert into linked list */ + dev->next = viddevs; + viddevs = dev; + + pthread_mutex_unlock(&vid_mutex); + + return fd; } -#endif /*WITHOUT_V4L */ +#endif /*WITHOUT_V4L */ @@ -842,25 +844,25 @@ return fd; int vid_start(struct context *cnt) { - struct config *conf = &cnt->conf; - int dev = -1; - - if (conf->netcam_url) { - dev = netcam_start(cnt); - if (dev < 0) { - netcam_cleanup(cnt->netcam, 1); - cnt->netcam = NULL; - } - } + struct config *conf = &cnt->conf; + int dev = -1; + + if (conf->netcam_url) { + dev = netcam_start(cnt); + if (dev < 0) { + netcam_cleanup(cnt->netcam, 1); + cnt->netcam = NULL; + } + } #ifdef WITHOUT_V4L - else - motion_log(LOG_ERR, 0, "%s: You must setup netcam_url", __FUNCTION__); + else + motion_log(LOG_ERR, 0, "%s: You must setup netcam_url", __FUNCTION__); #else - else - dev = vid_v4lx_start(cnt); -#endif /*WITHOUT_V4L */ + else + dev = vid_v4lx_start(cnt); +#endif /*WITHOUT_V4L */ - return dev; + return dev; } /** @@ -885,69 +887,67 @@ int vid_start(struct context *cnt) */ int vid_next(struct context *cnt, unsigned char *map) { - int ret = -2; - struct config *conf = &cnt->conf; + int ret = -2; + struct config *conf = &cnt->conf; - if (conf->netcam_url) { - if (cnt->video_dev == -1) - return NETCAM_GENERAL_ERROR; + if (conf->netcam_url) { + if (cnt->video_dev == -1) + return NETCAM_GENERAL_ERROR; - return netcam_next(cnt, map); - } + return netcam_next(cnt, map); + } #ifndef WITHOUT_V4L - /* We start a new block so we can make declarations without breaking - * gcc 2.95 or older - */ - { - struct video_dev *dev; - int width, height; - - /* NOTE: Since this is a capture, we need to use capture dimensions. */ - width = cnt->rotate_data.cap_width; - height = cnt->rotate_data.cap_height; - - pthread_mutex_lock(&vid_mutex); - dev = viddevs; - while (dev) { - if (dev->fd == cnt->video_dev) - break; - dev = dev->next; - } - pthread_mutex_unlock(&vid_mutex); - - if (dev == NULL) - return V4L_FATAL_ERROR; - - if (dev->owner != cnt->threadnr) { - pthread_mutex_lock(&dev->mutex); - dev->owner = cnt->threadnr; - dev->frames = conf->roundrobin_frames; - } + /* We start a new block so we can make declarations without breaking + * gcc 2.95 or older + */ + { + struct video_dev *dev; + int width, height; + + /* NOTE: Since this is a capture, we need to use capture dimensions. */ + width = cnt->rotate_data.cap_width; + height = cnt->rotate_data.cap_height; + + pthread_mutex_lock(&vid_mutex); + dev = viddevs; + while (dev) { + if (dev->fd == cnt->video_dev) + break; + dev = dev->next; + } + pthread_mutex_unlock(&vid_mutex); + + if (dev == NULL) + return V4L_FATAL_ERROR; + + if (dev->owner != cnt->threadnr) { + pthread_mutex_lock(&dev->mutex); + dev->owner = cnt->threadnr; + dev->frames = conf->roundrobin_frames; + } #ifdef MOTION_V4L2 - if (dev->v4l2) { - v4l2_set_input(cnt, dev, map, width, height, conf); - - ret = v4l2_next(cnt, dev, map, width, height); - } else { + if (dev->v4l2) { + v4l2_set_input(cnt, dev, map, width, height, conf); + ret = v4l2_next(cnt, dev, map, width, height); + } else { #endif - v4l_set_input(cnt, dev, map, width, height, conf->input, conf->norm, - conf->roundrobin_skip, conf->frequency, conf->tuner_number); - - ret = v4l_next(dev, map, width, height); + v4l_set_input(cnt, dev, map, width, height, conf->input, conf->norm, + conf->roundrobin_skip, conf->frequency, conf->tuner_number); + ret = v4l_next(dev, map, width, height); #ifdef MOTION_V4L2 - } + } #endif - if (--dev->frames <= 0) { - dev->owner = -1; - dev->frames = 0; - pthread_mutex_unlock(&dev->mutex); - } - - if (cnt->rotate_data.degrees > 0) { - /* rotate the image as specified */ - rotate_map(cnt, map); - } - } -#endif /*WITHOUT_V4L */ - return ret; + if (--dev->frames <= 0) { + dev->owner = -1; + dev->frames = 0; + pthread_mutex_unlock(&dev->mutex); + } + + /* rotate the image as specified */ + if (cnt->rotate_data.degrees > 0) + rotate_map(cnt, map); + + } +#endif /*WITHOUT_V4L */ + return ret; } diff --git a/video_freebsd.c b/video_freebsd.c index 11b3be6..cd1ac3a 100644 --- a/video_freebsd.c +++ b/video_freebsd.c @@ -1,9 +1,9 @@ -/* video_freebsd.c +/* video_freebsd.c * - * BSD Video stream functions for motion. - * Copyright 2004 by Angel Carpintero (ack@telefonica.net) - * This software is distributed under the GNU public license version 2 - * See also the file 'COPYING'. + * BSD Video stream functions for motion. + * Copyright 2004 by Angel Carpintero (ack@telefonica.net) + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. * */ @@ -40,7 +40,7 @@ #define VIDEO_MPEG 19 /* MPEG1/2 */ #define VIDEO_FMT_COUNT 20 -#define array_elem(x) (sizeof(x) / sizeof( (x)[0] )) +#define array_elem(x) (sizeof(x) / sizeof((x)[0])) static const struct camparam_st { int min, max, range, drv_min, drv_range, def; @@ -56,7 +56,7 @@ static const struct camparam_st { BT848_CONTRASTCENTER, }, { BT848_CHROMAMIN, (BT848_CHROMAMIN + BT848_CHROMARANGE), BT848_CHROMARANGE, - BT848_CHROMAREGMIN, (BT848_CHROMAREGMAX - BT848_CHROMAREGMIN + 1 ), + BT848_CHROMAREGMIN, (BT848_CHROMAREGMAX - BT848_CHROMAREGMIN + 1), BT848_CHROMACENTER, }, }; @@ -71,42 +71,42 @@ volatile sig_atomic_t bktr_frame_waiting; static void catchsignal(int sig) { - bktr_frame_waiting++; + bktr_frame_waiting++; } /* Not tested yet */ static void yuv422to420p(unsigned char *map, unsigned char *cap_map, int width, int height) { - unsigned char *src, *dest, *src2, *dest2; - int i, j; - - /* Create the Y plane */ - src = cap_map; - dest = map; - for (i = width * height; i; i--) { - *dest++ = *src; - src += 2; - } - /* Create U and V planes */ - src = cap_map + 1; - src2 = cap_map + width * 2 + 1; - dest = map + width* height; - dest2 = dest + (width * height) / 4; - for (i = height / 2; i; i--) { - for (j = width / 2; j; j--) { - *dest = ((int)*src + (int)*src2) / 2; - src += 2; - src2 += 2; - dest++; - *dest2 = ((int)*src + (int)*src2) / 2; - src += 2; - src2 += 2; - dest2++; - } - src += width * 2; - src2 += width * 2; - } + unsigned char *src, *dest, *src2, *dest2; + int i, j; + + /* Create the Y plane */ + src = cap_map; + dest = map; + for (i = width * height; i; i--) { + *dest++ = *src; + src += 2; + } + /* Create U and V planes */ + src = cap_map + 1; + src2 = cap_map + width * 2 + 1; + dest = map + width* height; + dest2 = dest + (width * height) / 4; + for (i = height / 2; i; i--) { + for (j = width / 2; j; j--) { + *dest = ((int)*src + (int)*src2) / 2; + src += 2; + src2 += 2; + dest++; + *dest2 = ((int)*src + (int)*src2) / 2; + src += 2; + src2 += 2; + dest2++; + } + src += width * 2; + src2 += width * 2; + } } @@ -114,42 +114,42 @@ static void yuv422to420p(unsigned char *map, unsigned char *cap_map, int width, static void rgb24toyuv420p(unsigned char *map, unsigned char *cap_map, int width, int height) { - unsigned char *y, *u, *v; - unsigned char *r, *g, *b; - int i, loop; - - b = cap_map; - g = b + 1; - r = g + 1; - y = map; - u = y + width * height; - v = u + (width * height) / 4; - memset(u, 0, width * height / 4); - memset(v, 0, width * height / 4); - - for (loop = 0; loop < height; loop++) { - for (i = 0; i < width; i += 2) { - *y++ = (9796 ** r + 19235 ** g + 3736 ** b) >> 15; - *u += ((-4784 ** r - 9437 ** g + 14221 ** b) >> 17) + 32; - *v += ((20218 ** r - 16941**g - 3277 ** b) >> 17) + 32; - r += 3; - g += 3; - b += 3; - *y++ = (9796 ** r + 19235 ** g + 3736 ** b) >> 15; - *u += ((-4784 ** r - 9437 ** g + 14221 ** b) >> 17) + 32; - *v += ((20218 ** r - 16941 ** g - 3277 ** b) >> 17) + 32; - r += 3; - g += 3; - b += 3; - u++; - v++; - } - - if ((loop & 1) == 0) { - u -= width / 2; - v -= width / 2; - } - } + unsigned char *y, *u, *v; + unsigned char *r, *g, *b; + int i, loop; + + b = cap_map; + g = b + 1; + r = g + 1; + y = map; + u = y + width * height; + v = u + (width * height) / 4; + memset(u, 0, width * height / 4); + memset(v, 0, width * height / 4); + + for (loop = 0; loop < height; loop++) { + for (i = 0; i < width; i += 2) { + *y++ = (9796 ** r + 19235 ** g + 3736 ** b) >> 15; + *u += ((-4784 ** r - 9437 ** g + 14221 ** b) >> 17) + 32; + *v += ((20218 ** r - 16941**g - 3277 ** b) >> 17) + 32; + r += 3; + g += 3; + b += 3; + *y++ = (9796 ** r + 19235 ** g + 3736 ** b) >> 15; + *u += ((-4784 ** r - 9437 ** g + 14221 ** b) >> 17) + 32; + *v += ((20218 ** r - 16941 ** g - 3277 ** b) >> 17) + 32; + r += 3; + g += 3; + b += 3; + u++; + v++; + } + + if ((loop & 1) == 0) { + u -= width / 2; + v -= width / 2; + } + } } @@ -160,165 +160,164 @@ static void rgb24toyuv420p(unsigned char *map, unsigned char *cap_map, int width /* NOT TESTED YET FIXME */ /* -static int camparam_normalize( int param, int cfg_value, int *ioctl_val ) +static int camparam_normalize(int param, int cfg_value, int *ioctl_val) { - int val; - - cfg_value = MIN( CamParams[ param ].max, MAX( CamParams[ param ].min, cfg_value ) ); - val = (cfg_value - CamParams[ param ].min ) / - (CamParams[ param ].range + 0.01) * CamParams[param].drv_range + CamParams[param].drv_min; - val = MAX( CamParams[ param ].min, - MIN( CamParams[ param ].drv_min + CamParams[ param ].drv_range-1, val )); - *ioctl_val = val; - return cfg_value; + int val; + + cfg_value = MIN(CamParams[ param ].max, MAX(CamParams[ param ].min, cfg_value)); + val = (cfg_value - CamParams[ param ].min) / + (CamParams[ param ].range + 0.01) * CamParams[param].drv_range + CamParams[param].drv_min; + val = MAX(CamParams[ param ].min, + MIN(CamParams[ param ].drv_min + CamParams[ param ].drv_range-1, val)); + *ioctl_val = val; + return cfg_value; } */ -static int set_hue( int viddev, int new_hue ) +static int set_hue(int viddev, int new_hue) { - signed char ioctlval = new_hue; + signed char ioctlval = new_hue; - if (ioctl(viddev, METEORSHUE, &ioctlval) < 0) { - motion_log(LOG_ERR, 1, "%s: METEORSHUE Error setting hue [%d]", __FUNCTION__, new_hue); - return -1; - } + if (ioctl(viddev, METEORSHUE, &ioctlval) < 0) { + motion_log(LOG_ERR, 1, "%s: METEORSHUE Error setting hue [%d]", __FUNCTION__, new_hue); + return -1; + } - if (debug_level >= CAMERA_VIDEO) - motion_log(-1, 0, "%s: to [%d]", __FUNCTION__, ioctlval); + if (debug_level >= CAMERA_VIDEO) + motion_log(-1, 0, "%s: to [%d]", __FUNCTION__, ioctlval); - return ioctlval; + return ioctlval; } static int get_hue(int viddev , int *hue) { - signed char ioctlval; - - if (ioctl(viddev, METEORGHUE, &ioctlval) < 0) { - motion_log(LOG_ERR, 1, "%s: METEORGHUE Error getting hue", __FUNCTION__); - return -1; - } - - if (debug_level >= CAMERA_VIDEO) - motion_log(-1, 0, "%s: to [%d]", __FUNCTION__, ioctlval); - - *hue = ioctlval; - return ioctlval; + signed char ioctlval; + + if (ioctl(viddev, METEORGHUE, &ioctlval) < 0) { + motion_log(LOG_ERR, 1, "%s: METEORGHUE Error getting hue", __FUNCTION__); + return -1; + } + + if (debug_level >= CAMERA_VIDEO) + motion_log(-1, 0, "%s: to [%d]", __FUNCTION__, ioctlval); + + *hue = ioctlval; + return ioctlval; } static int set_saturation(int viddev, int new_saturation) { - unsigned char ioctlval= new_saturation; + unsigned char ioctlval= new_saturation; - if (ioctl(viddev, METEORSCSAT, &ioctlval) < 0) { - motion_log(LOG_ERR, 1, "%s: METEORSCSAT Error setting saturation [%d]", - __FUNCTION__, new_saturation); - return -1; - } + if (ioctl(viddev, METEORSCSAT, &ioctlval) < 0) { + motion_log(LOG_ERR, 1, "%s: METEORSCSAT Error setting saturation [%d]", + __FUNCTION__, new_saturation); + return -1; + } - if (debug_level >= CAMERA_VIDEO) - motion_log(-1, 0, "%s: to [%d]", __FUNCTION__, ioctlval); + if (debug_level >= CAMERA_VIDEO) + motion_log(-1, 0, "%s: to [%d]", __FUNCTION__, ioctlval); - return ioctlval; + return ioctlval; } static int get_saturation(int viddev , int *saturation) { - unsigned char ioctlval; - - if (ioctl(viddev, METEORGCSAT, &ioctlval) < 0) { - - motion_log(LOG_ERR, 1, "%s: METEORGCSAT Error getting saturation", __FUNCTION__); - return -1; - } - - if (debug_level >= CAMERA_VIDEO) - motion_log(-1, 0, "%s: to [%d]", __FUNCTION__, ioctlval); - - *saturation = ioctlval; - return ioctlval; + unsigned char ioctlval; + + if (ioctl(viddev, METEORGCSAT, &ioctlval) < 0) { + motion_log(LOG_ERR, 1, "%s: METEORGCSAT Error getting saturation", __FUNCTION__); + return -1; + } + + if (debug_level >= CAMERA_VIDEO) + motion_log(-1, 0, "%s: to [%d]", __FUNCTION__, ioctlval); + + *saturation = ioctlval; + return ioctlval; } static int set_contrast(int viddev, int new_contrast) { - unsigned char ioctlval = new_contrast; + unsigned char ioctlval = new_contrast; - if (ioctl(viddev, METEORSCONT, &ioctlval) < 0) { - motion_log(LOG_ERR, 1, "%s: METEORSCONT Error setting contrast [%d]", __FUNCTION__, new_contrast); - return 0; - } + if (ioctl(viddev, METEORSCONT, &ioctlval) < 0) { + motion_log(LOG_ERR, 1, "%s: METEORSCONT Error setting contrast [%d]", __FUNCTION__, new_contrast); + return 0; + } - if (debug_level >= CAMERA_VIDEO) - motion_log(-1, 0, "%s: to [%d]", __FUNCTION__, ioctlval); + if (debug_level >= CAMERA_VIDEO) + motion_log(-1, 0, "%s: to [%d]", __FUNCTION__, ioctlval); - return ioctlval; + return ioctlval; } static int get_contrast(int viddev, int *contrast) { - unsigned char ioctlval; - - if (ioctl (viddev, METEORGCONT, &ioctlval ) < 0) { - motion_log(LOG_ERR, 1, "%s: METEORGCONT Error getting contrast", __FUNCTION__); - return -1; - } - - if (debug_level >= CAMERA_VIDEO) - motion_log(-1, 0, "%s: to [%d]", __FUNCTION__, ioctlval); - - *contrast = ioctlval; - return ioctlval; + unsigned char ioctlval; + + if (ioctl(viddev, METEORGCONT, &ioctlval) < 0) { + motion_log(LOG_ERR, 1, "%s: METEORGCONT Error getting contrast", __FUNCTION__); + return -1; + } + + if (debug_level >= CAMERA_VIDEO) + motion_log(-1, 0, "%s: to [%d]", __FUNCTION__, ioctlval); + + *contrast = ioctlval; + return ioctlval; } static int set_brightness(int viddev, int new_bright) { - unsigned char ioctlval = new_bright; + unsigned char ioctlval = new_bright; - if (ioctl(viddev, METEORSBRIG, &ioctlval) < 0) { - motion_log(LOG_ERR, 1, "%s: METEORSBRIG brightness [%d]", __FUNCTION__, new_bright); - return -1; - } + if (ioctl(viddev, METEORSBRIG, &ioctlval) < 0) { + motion_log(LOG_ERR, 1, "%s: METEORSBRIG brightness [%d]", __FUNCTION__, new_bright); + return -1; + } - if (debug_level >= CAMERA_VIDEO) - motion_log(-1, 0, "%s: to [%d]", __FUNCTION__, ioctlval); - - return ioctlval; + if (debug_level >= CAMERA_VIDEO) + motion_log(-1, 0, "%s: to [%d]", __FUNCTION__, ioctlval); + + return ioctlval; } static int get_brightness(int viddev, int *brightness) { - unsigned char ioctlval; - - if (ioctl(viddev, METEORGBRIG, &ioctlval) < 0) { - motion_log(LOG_ERR, 1, "%s: METEORGBRIG getting brightness", __FUNCTION__); - return -1; - } - - if (debug_level >= CAMERA_VIDEO) - motion_log(-1, 0, "%s: to [%d]", __FUNCTION__, ioctlval); - - *brightness = ioctlval; - return ioctlval; + unsigned char ioctlval; + + if (ioctl(viddev, METEORGBRIG, &ioctlval) < 0) { + motion_log(LOG_ERR, 1, "%s: METEORGBRIG getting brightness", __FUNCTION__); + return -1; + } + + if (debug_level >= CAMERA_VIDEO) + motion_log(-1, 0, "%s: to [%d]", __FUNCTION__, ioctlval); + + *brightness = ioctlval; + return ioctlval; } // Set channel needed ? FIXME /* -static int set_channel( struct video_dev *viddev, int new_channel ) +static int set_channel(struct video_dev *viddev, int new_channel) { - int ioctlval; + int ioctlval; - ioctlval = new_channel; - if( ioctl( viddev->fd_tuner, TVTUNER_SETCHNL, &ioctlval ) < 0 ) { - motion_log(LOG_ERR, 1, "Error channel %d", ioctlval); - return -1; - } else { - motion_log(LOG_DEBUG, 0, "channel set to %d", ioctlval); - } + ioctlval = new_channel; + if (ioctl(viddev->fd_tuner, TVTUNER_SETCHNL, &ioctlval) < 0) { + motion_log(LOG_ERR, 1, "Error channel %d", ioctlval); + return -1; + } else { + motion_log(LOG_DEBUG, 0, "channel set to %d", ioctlval); + } - viddev->channel = new_channel; + viddev->channel = new_channel; - return 0; + return 0; } */ @@ -326,114 +325,120 @@ static int set_channel( struct video_dev *viddev, int new_channel ) static int set_freq(struct video_dev *viddev, unsigned long freq) { - int tuner_fd = viddev->fd_tuner; - int old_audio; - - motion_log(LOG_DEBUG, 0, "%s: Not implemented", __FUNCTION__); - - return 0; - - /* HACK maybe not need it , but seems that is needed to mute before changing frequency */ - - if (ioctl(tuner_fd, BT848_GAUDIO, &old_audio) < 0) { - motion_log(LOG_ERR, 1, "%s: BT848_GAUDIO", __FUNCTION__); - return -1; - } - - if (ioctl(tuner_fd, TVTUNER_SETFREQ, &freq) < 0) { - motion_log(LOG_ERR, 1, "%s: Tuning (TVTUNER_SETFREQ) failed , freq [%lu]", __FUNCTION__, freq); - return -1; - } - - old_audio &= AUDIO_MUTE; - if (old_audio) { - old_audio = AUDIO_MUTE; - if (ioctl(tuner_fd , BT848_SAUDIO, &old_audio) < 0) { - motion_log(LOG_ERR, 1, "%s: BT848_SAUDIO %i", __FUNCTION__, old_audio); - return -1; - } - } - - return 0; + int tuner_fd = viddev->fd_tuner; + int old_audio; + + motion_log(LOG_DEBUG, 0, "%s: Not implemented", __FUNCTION__); + + return 0; + + /* HACK maybe not need it , but seems that is needed to mute before changing frequency */ + + if (ioctl(tuner_fd, BT848_GAUDIO, &old_audio) < 0) { + motion_log(LOG_ERR, 1, "%s: BT848_GAUDIO", __FUNCTION__); + return -1; + } + + if (ioctl(tuner_fd, TVTUNER_SETFREQ, &freq) < 0) { + motion_log(LOG_ERR, 1, "%s: Tuning (TVTUNER_SETFREQ) failed , freq [%lu]", __FUNCTION__, freq); + return -1; + } + + old_audio &= AUDIO_MUTE; + if (old_audio) { + old_audio = AUDIO_MUTE; + if (ioctl(tuner_fd , BT848_SAUDIO, &old_audio) < 0) { + motion_log(LOG_ERR, 1, "%s: BT848_SAUDIO %i", __FUNCTION__, old_audio); + return -1; + } + } + + return 0; } /* set the input to capture images , could be tuner (METEOR_INPUT_DEV1) or any of others input : - RCA/COMPOSITE1 (METEOR_INPUT_DEV0) - COMPOSITE2/S-VIDEO (METEOR_INPUT_DEV2) - S-VIDEO (METEOR_INPUT_DEV3) - VBI ?! (METEOR_INPUT_DEV_SVIDEO) + RCA/COMPOSITE1 (METEOR_INPUT_DEV0) + COMPOSITE2/S-VIDEO (METEOR_INPUT_DEV2) + S-VIDEO (METEOR_INPUT_DEV3) + VBI ?! (METEOR_INPUT_DEV_SVIDEO) */ static int set_input(struct video_dev *viddev, unsigned short input) { - int actport; - int portdata[] = { METEOR_INPUT_DEV0, METEOR_INPUT_DEV1, - METEOR_INPUT_DEV2, METEOR_INPUT_DEV3, - METEOR_INPUT_DEV_SVIDEO }; - - if (input >= array_elem(portdata)) { - motion_log(LOG_INFO, 0, "%s: Channel Port %d out of range (0-4)", __FUNCTION__, input); - return -1; - } - - actport = portdata[ input ]; - if (ioctl(viddev->fd_bktr, METEORSINPUT, &actport) < 0) { - if (input != IN_DEFAULT) { - motion_log(LOG_INFO, 1, "%s: METEORSINPUT %d invalid - Trying default %d", - __FUNCTION__, input, IN_DEFAULT); - input = IN_DEFAULT; - actport = portdata[ input ]; - if (ioctl(viddev->fd_bktr, METEORSINPUT, &actport) < 0) { - motion_log(LOG_ERR, 1, "%s: METEORSINPUT %d init", __FUNCTION__, input); - return -1; - } - } else { - motion_log(LOG_ERR, 1, "%s: METEORSINPUT %d init", __FUNCTION__, input); - return -1; - } - } - - if (debug_level >= CAMERA_VIDEO) - motion_log(-1, 0, "%s: to [%d]", __FUNCTION__, input); - - return input; + int actport; + int portdata[] = { METEOR_INPUT_DEV0, METEOR_INPUT_DEV1, + METEOR_INPUT_DEV2, METEOR_INPUT_DEV3, + METEOR_INPUT_DEV_SVIDEO }; + + if (input >= array_elem(portdata)) { + motion_log(LOG_INFO, 0, "%s: Channel Port %d out of range (0-4)", __FUNCTION__, input); + return -1; + } + + actport = portdata[ input ]; + if (ioctl(viddev->fd_bktr, METEORSINPUT, &actport) < 0) { + if (input != IN_DEFAULT) { + motion_log(LOG_INFO, 1, "%s: METEORSINPUT %d invalid - Trying default %d", + __FUNCTION__, input, IN_DEFAULT); + input = IN_DEFAULT; + actport = portdata[ input ]; + if (ioctl(viddev->fd_bktr, METEORSINPUT, &actport) < 0) { + motion_log(LOG_ERR, 1, "%s: METEORSINPUT %d init", __FUNCTION__, input); + return -1; + } + } else { + motion_log(LOG_ERR, 1, "%s: METEORSINPUT %d init", __FUNCTION__, input); + return -1; + } + } + + if (debug_level >= CAMERA_VIDEO) + motion_log(-1, 0, "%s: to [%d]", __FUNCTION__, input); + + return input; } static int set_geometry(struct video_dev *viddev, int width, int height) { - struct meteor_geomet geom; - int h_max; - - geom.columns = width; - geom.rows = height; - - geom.oformat = METEOR_GEO_YUV_422 | METEOR_GEO_YUV_12; - - - switch (viddev->norm) { - case PAL: h_max = PAL_HEIGHT; - case NTSC: h_max = NTSC_HEIGHT; - case SECAM: h_max = SECAM_HEIGHT; - default: h_max = PAL_HEIGHT; - } - - if (height <= h_max / 2) { - geom.oformat |= METEOR_GEO_EVEN_ONLY; - } - - geom.frames = 1; - - if (ioctl(viddev->fd_bktr, METEORSETGEO, &geom) < 0) { - motion_log(LOG_ERR, 1, "%s: Couldn't set the geometry", __FUNCTION__); - return -1; - } - - if (debug_level >= CAMERA_VIDEO) - motion_log(-1, 0, "%s: to [%d/%d] Norm %d", __FUNCTION__, width, height, viddev->norm); - - return 0; + struct meteor_geomet geom; + int h_max; + + geom.columns = width; + geom.rows = height; + + geom.oformat = METEOR_GEO_YUV_422 | METEOR_GEO_YUV_12; + + + switch (viddev->norm) { + case PAL: + h_max = PAL_HEIGHT; + break; + case NTSC: + h_max = NTSC_HEIGHT; + break; + case SECAM: + h_max = SECAM_HEIGHT; + break; + default: + h_max = PAL_HEIGHT; + } + + if (height <= h_max / 2) + geom.oformat |= METEOR_GEO_EVEN_ONLY; + + geom.frames = 1; + + if (ioctl(viddev->fd_bktr, METEORSETGEO, &geom) < 0) { + motion_log(LOG_ERR, 1, "%s: Couldn't set the geometry", __FUNCTION__); + return -1; + } + + if (debug_level >= CAMERA_VIDEO) + motion_log(-1, 0, "%s: to [%d/%d] Norm %d", __FUNCTION__, width, height, viddev->norm); + + return 0; } /* @@ -442,151 +447,151 @@ static int set_geometry(struct video_dev *viddev, int width, int height) static int set_input_format(struct video_dev *viddev, unsigned short newformat) { - int input_format[] = { NORM_PAL_NEW, NORM_NTSC_NEW, NORM_SECAM_NEW, NORM_DEFAULT_NEW}; - int format; + int input_format[] = { NORM_PAL_NEW, NORM_NTSC_NEW, NORM_SECAM_NEW, NORM_DEFAULT_NEW}; + int format; - if (newformat >= array_elem( input_format )) { - motion_log(LOG_WARNING, 0, "%s: Input format %d out of range (0-2)", __FUNCTION__, newformat); - return -1; - } - - format = input_format[newformat]; - - if( ioctl( viddev->fd_bktr, BT848SFMT, &format ) < 0 ) { - motion_log(LOG_ERR, 1, "%s: BT848SFMT, Couldn't set the input format , try again with default", - __FUNCTION__); - format = NORM_DEFAULT_NEW; - newformat = 3; - - if( ioctl( viddev->fd_bktr, BT848SFMT, &format ) < 0 ) { - motion_log(LOG_ERR, 1, "%s: BT848SFMT, Couldn't set the input format either default", - __FUNCTION__); - return -1; - } - } - - if (debug_level >= CAMERA_VIDEO) - motion_log(-1, 0, "%s: to %d", __FUNCTION__, newformat); - - return newformat; + if (newformat >= array_elem(input_format)) { + motion_log(LOG_WARNING, 0, "%s: Input format %d out of range (0-2)", __FUNCTION__, newformat); + return -1; + } + + format = input_format[newformat]; + + if (ioctl(viddev->fd_bktr, BT848SFMT, &format) < 0) { + motion_log(LOG_ERR, 1, "%s: BT848SFMT, Couldn't set the input format , try again with default", + __FUNCTION__); + format = NORM_DEFAULT_NEW; + newformat = 3; + + if (ioctl(viddev->fd_bktr, BT848SFMT, &format) < 0) { + motion_log(LOG_ERR, 1, "%s: BT848SFMT, Couldn't set the input format either default", + __FUNCTION__); + return -1; + } + } + + if (debug_level >= CAMERA_VIDEO) + motion_log(-1, 0, "%s: to %d", __FUNCTION__, newformat); + + return newformat; } /* -statict int setup_pixelformat( int bktr ) +statict int setup_pixelformat(int bktr) { - int i; - struct meteor_pixfmt p; - int format=-1; - - for( i = 0; ; i++ ){ - p.index = i; - if( ioctl( bktr, METEORGSUPPIXFMT, &p ) < 0 ){ - if( errno == EINVAL ) - break; - motion_log(LOG_ERR, 1, "METEORGSUPPIXFMT getting pixformat %d", i); - return -1; - } - - - // Hack from xawtv 4.x - - switch ( p.type ){ - case METEOR_PIXTYPE_RGB: - motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_RGB"); - switch(p.masks[0]) { - case 31744: // 15 bpp - format = p.swap_bytes ? VIDEO_RGB15_LE : VIDEO_RGB15_BE; - motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_RGB VIDEO_RGB15"); - break; - case 63488: // 16 bpp - format = p.swap_bytes ? VIDEO_RGB16_LE : VIDEO_RGB16_BE; - motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_RGB VIDEO_RGB16"); - break; - case 16711680: // 24/32 bpp - if (p.Bpp == 3 && p.swap_bytes == 1) { - format = VIDEO_BGR24; - motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_RGB VIDEO_BGR24"); - } else if (p.Bpp == 4 && p.swap_bytes == 1 && p.swap_shorts == 1) { - format = VIDEO_BGR32; - motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_RGB VIDEO_BGR32"); - } else if (p.Bpp == 4 && p.swap_bytes == 0 && p.swap_shorts == 0) { - format = VIDEO_RGB32; - motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_RGB VIDEO_RGB32"); - } - } - break; - case METEOR_PIXTYPE_YUV: - format = VIDEO_YUV422P; - motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_YUV"); - break; - case METEOR_PIXTYPE_YUV_12: - format = VIDEO_YUV422P; - motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_YUV_12"); - break; - case METEOR_PIXTYPE_YUV_PACKED: - format = VIDEO_YUV422P; - motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_YUV_PACKED"); - break; - - } - - if( p.type == METEOR_PIXTYPE_RGB && p.Bpp == 3 ){ - // Found a good pixeltype -- set it up - if( ioctl( bktr, METEORSACTPIXFMT, &i ) < 0 ){ - motion_log(LOG_WARNING, 1, "METEORSACTPIXFMT etting pixformat METEOR_PIXTYPE_RGB Bpp == 3"); - // Not immediately fatal - } - motion_log(LOG_DEBUG, 0, "input format METEOR_PIXTYPE_RGB %i", i); - format = i; - } - - if( p.type == METEOR_PIXTYPE_YUV_PACKED ){ - // Found a good pixeltype -- set it up - if( ioctl( bktr, METEORSACTPIXFMT, &i ) < 0 ){ - motion_log(LOG_WARNING, 1, "METEORSACTPIXFMT setting pixformat METEOR_PIXTYPE_YUV_PACKED"); - // Not immediately fatal - } - motion_log(LOG_DEBUG, 0, "input format METEOR_PIXTYPE_YUV_PACKED %i", i); - format = i; - } - - } - - return format; + int i; + struct meteor_pixfmt p; + int format=-1; + + for (i = 0; ; i++) { + p.index = i; + if (ioctl(bktr, METEORGSUPPIXFMT, &p) < 0) { + if (errno == EINVAL) + break; + motion_log(LOG_ERR, 1, "METEORGSUPPIXFMT getting pixformat %d", i); + return -1; + } + + + // Hack from xawtv 4.x + + switch (p.type) { + case METEOR_PIXTYPE_RGB: + motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_RGB"); + switch (p.masks[0]) { + case 31744: // 15 bpp + format = p.swap_bytes ? VIDEO_RGB15_LE : VIDEO_RGB15_BE; + motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_RGB VIDEO_RGB15"); + break; + case 63488: // 16 bpp + format = p.swap_bytes ? VIDEO_RGB16_LE : VIDEO_RGB16_BE; + motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_RGB VIDEO_RGB16"); + break; + case 16711680: // 24/32 bpp + if (p.Bpp == 3 && p.swap_bytes == 1) { + format = VIDEO_BGR24; + motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_RGB VIDEO_BGR24"); + } else if (p.Bpp == 4 && p.swap_bytes == 1 && p.swap_shorts == 1) { + format = VIDEO_BGR32; + motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_RGB VIDEO_BGR32"); + } else if (p.Bpp == 4 && p.swap_bytes == 0 && p.swap_shorts == 0) { + format = VIDEO_RGB32; + motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_RGB VIDEO_RGB32"); + } + } + break; + case METEOR_PIXTYPE_YUV: + format = VIDEO_YUV422P; + motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_YUV"); + break; + case METEOR_PIXTYPE_YUV_12: + format = VIDEO_YUV422P; + motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_YUV_12"); + break; + case METEOR_PIXTYPE_YUV_PACKED: + format = VIDEO_YUV422P; + motion_log(-1, 0, "setup_pixelformat METEOR_PIXTYPE_YUV_PACKED"); + break; + + } + + if (p.type == METEOR_PIXTYPE_RGB && p.Bpp == 3) { + // Found a good pixeltype -- set it up + if (ioctl(bktr, METEORSACTPIXFMT, &i) < 0) { + motion_log(LOG_WARNING, 1, "METEORSACTPIXFMT etting pixformat METEOR_PIXTYPE_RGB Bpp == 3"); + // Not immediately fatal + } + motion_log(LOG_DEBUG, 0, "input format METEOR_PIXTYPE_RGB %i", i); + format = i; + } + + if (p.type == METEOR_PIXTYPE_YUV_PACKED) { + // Found a good pixeltype -- set it up + if (ioctl(bktr, METEORSACTPIXFMT, &i) < 0) { + motion_log(LOG_WARNING, 1, "METEORSACTPIXFMT setting pixformat METEOR_PIXTYPE_YUV_PACKED"); + // Not immediately fatal + } + motion_log(LOG_DEBUG, 0, "input format METEOR_PIXTYPE_YUV_PACKED %i", i); + format = i; + } + + } + + return format; } */ static void v4l_picture_controls(struct context *cnt, struct video_dev *viddev) { - int dev = viddev->fd_bktr; - - if ( (cnt->conf.contrast) && (cnt->conf.contrast != viddev->contrast) ) { - set_contrast(dev, cnt->conf.contrast); - viddev->contrast = cnt->conf.contrast; - } - - if ( (cnt->conf.hue) && (cnt->conf.hue != viddev->hue) ) { - set_hue(dev, cnt->conf.hue); - viddev->hue = cnt->conf.hue; - } - - if ( (cnt->conf.brightness) && - (cnt->conf.brightness != viddev->brightness)) { - set_brightness(dev, cnt->conf.brightness); - viddev->brightness = cnt->conf.brightness; - } - - if ( (cnt->conf.saturation ) && - (cnt->conf.saturation != viddev->saturation) ) { - set_saturation(dev, cnt->conf.saturation); - viddev->saturation = cnt->conf.saturation; - } + int dev = viddev->fd_bktr; + + if ((cnt->conf.contrast) && (cnt->conf.contrast != viddev->contrast)) { + set_contrast(dev, cnt->conf.contrast); + viddev->contrast = cnt->conf.contrast; + } + + if ((cnt->conf.hue) && (cnt->conf.hue != viddev->hue)) { + set_hue(dev, cnt->conf.hue); + viddev->hue = cnt->conf.hue; + } + + if ((cnt->conf.brightness) && + (cnt->conf.brightness != viddev->brightness)) { + set_brightness(dev, cnt->conf.brightness); + viddev->brightness = cnt->conf.brightness; + } + + if ((cnt->conf.saturation) && + (cnt->conf.saturation != viddev->saturation)) { + set_saturation(dev, cnt->conf.saturation); + viddev->saturation = cnt->conf.saturation; + } } /******************************************************************************************* - Video capture routines + Video capture routines - set input - setup_pixelformat @@ -603,178 +608,179 @@ static void v4l_picture_controls(struct context *cnt, struct video_dev *viddev) static unsigned char *v4l_start(struct video_dev *viddev, int width, int height, unsigned short input, unsigned short norm, unsigned long freq) { - int dev_bktr = viddev->fd_bktr; - struct sigaction act, old; - //int dev_tunner = viddev->fd_tuner; - /* to ensure that all device will be support the capture mode - _TODO_ : Autodected the best capture mode . - */ - int dummy = 1; -// int pixelformat = BSD_VIDFMT_I420; - - void *map; - - /* if we have choose the tuner is needed to setup the frequency */ - if ( (viddev->tuner_device != NULL) && ( input == IN_TV ) ) { - if (!freq) { - motion_log(LOG_ERR, 0, "%s: Not valid Frequency [%lu] for Source input [%i]", - __FUNCTION__, freq, input); - return (NULL); - }else if (set_freq(viddev, freq) == -1) { - motion_log(LOG_ERR, 0, "%s: Frequency [%lu] Source input [%i]", - __FUNCTION__, freq, input); - return (NULL); - } - } - - /* FIXME if we set as input tuner , we need to set option for tuner not for bktr */ - - if ((dummy = set_input(viddev, input)) == -1) { - motion_log(LOG_ERR, 0, "%s: set input [%d]", __FUNCTION__, input); - return (NULL); - } - - viddev->input = dummy; - - if ((dummy = set_input_format(viddev, norm)) == -1) { - motion_log(LOG_ERR, 0, "%s: set input format [%d]", __FUNCTION__, norm); - return (NULL); - } - - viddev->norm = dummy; - - if (set_geometry(viddev, width, height) == -1) { - motion_log(LOG_ERR, 0, "%s: set geometry [%d]x[%d]", __FUNCTION__, width, height); - return (NULL); - } -/* - if (ioctl(dev_bktr, METEORSACTPIXFMT, &pixelformat) < 0 ){ - motion_log(LOG_ERR, 1, "set encoding method BSD_VIDFMT_I420"); - return(NULL); + int dev_bktr = viddev->fd_bktr; + struct sigaction act, old; + //int dev_tunner = viddev->fd_tuner; + /* to ensure that all device will be support the capture mode + _TODO_ : Autodected the best capture mode . + */ + int dummy = 1; + // int pixelformat = BSD_VIDFMT_I420; + + void *map; + + /* if we have choose the tuner is needed to setup the frequency */ + if ((viddev->tuner_device != NULL) && (input == IN_TV)) { + if (!freq) { + motion_log(LOG_ERR, 0, "%s: Not valid Frequency [%lu] for Source input [%i]", + __FUNCTION__, freq, input); + return (NULL); + } else if (set_freq(viddev, freq) == -1) { + motion_log(LOG_ERR, 0, "%s: Frequency [%lu] Source input [%i]", + __FUNCTION__, freq, input); + return (NULL); } + } + + /* FIXME if we set as input tuner , we need to set option for tuner not for bktr */ + if ((dummy = set_input(viddev, input)) == -1) { + motion_log(LOG_ERR, 0, "%s: set input [%d]", __FUNCTION__, input); + return (NULL); + } - NEEDED !? FIXME + viddev->input = dummy; - if ( setup_pixelformat(viddev) == -1) { - return (NULL); - } -*/ + if ((dummy = set_input_format(viddev, norm)) == -1) { + motion_log(LOG_ERR, 0, "%s: set input format [%d]", __FUNCTION__, norm); + return (NULL); + } + + viddev->norm = dummy; + + if (set_geometry(viddev, width, height) == -1) { + motion_log(LOG_ERR, 0, "%s: set geometry [%d]x[%d]", __FUNCTION__, width, height); + return (NULL); + } + + /* + if (ioctl(dev_bktr, METEORSACTPIXFMT, &pixelformat) < 0) { + motion_log(LOG_ERR, 1, "set encoding method BSD_VIDFMT_I420"); + return(NULL); + } - if (freq) { - if (debug_level >= CAMERA_DEBUG) - motion_log(-1, 0, "%s: Frequency set (no implemented yet", __FUNCTION__); - /* - TODO missing implementation - set_channelset(viddev); - set_channel(viddev); - if (set_freq (viddev, freq) == -1) { - return (NULL); - } - */ - } - - - /* set capture mode and capture buffers */ - - /* That is the buffer size for capture images , - so is dependent of color space of input format / FIXME */ - - viddev->v4l_bufsize = (((width * height * 3 / 2)) * sizeof(unsigned char *)); - viddev->v4l_fmt = VIDEO_PALETTE_YUV420P; - - - map = mmap((caddr_t)0, viddev->v4l_bufsize, PROT_READ|PROT_WRITE, MAP_SHARED, dev_bktr, (off_t)0); - - if (map == MAP_FAILED){ - motion_log(LOG_ERR, 1, "%s: mmap failed", __FUNCTION__); - return (NULL); - } - - /* FIXME double buffer */ - if (0) { - viddev->v4l_maxbuffer = 2; - viddev->v4l_buffers[0] = map; - viddev->v4l_buffers[1] = (unsigned char *)map + 0; /* 0 is not valid just a test */ - //viddev->v4l_buffers[1] = map+vid_buf.offsets[1]; - } else { - viddev->v4l_buffers[0] = map; - viddev->v4l_maxbuffer = 1; - } - - viddev->v4l_curbuffer = 0; - - /* Clear the buffer */ - - if (ioctl(dev_bktr, BT848SCBUF, &dummy) < 0) { - motion_log(LOG_ERR, 1, "%s: BT848SCBUF", __FUNCTION__); - return NULL; + + NEEDED !? FIXME + + if (setup_pixelformat(viddev) == -1) { + return (NULL); + } + */ + + if (freq) { + if (debug_level >= CAMERA_DEBUG) + motion_log(-1, 0, "%s: Frequency set (no implemented yet", __FUNCTION__); + /* + TODO missing implementation + set_channelset(viddev); + set_channel(viddev); + if (set_freq (viddev, freq) == -1) { + return (NULL); } + */ + } + + + /* set capture mode and capture buffers */ + + /* That is the buffer size for capture images , + so is dependent of color space of input format / FIXME */ + + viddev->v4l_bufsize = (((width * height * 3 / 2)) * sizeof(unsigned char *)); + viddev->v4l_fmt = VIDEO_PALETTE_YUV420P; + + + map = mmap((caddr_t)0, viddev->v4l_bufsize, PROT_READ|PROT_WRITE, MAP_SHARED, dev_bktr, (off_t)0); - /* signal handler to know when data is ready to be read() */ + if (map == MAP_FAILED) { + motion_log(LOG_ERR, 1, "%s: mmap failed", __FUNCTION__); + return (NULL); + } + + /* FIXME double buffer */ + if (0) { + viddev->v4l_maxbuffer = 2; + viddev->v4l_buffers[0] = map; + viddev->v4l_buffers[1] = (unsigned char *)map + 0; /* 0 is not valid just a test */ + //viddev->v4l_buffers[1] = map+vid_buf.offsets[1]; + } else { + viddev->v4l_buffers[0] = map; + viddev->v4l_maxbuffer = 1; + } + + viddev->v4l_curbuffer = 0; + + /* Clear the buffer */ + + if (ioctl(dev_bktr, BT848SCBUF, &dummy) < 0) { + motion_log(LOG_ERR, 1, "%s: BT848SCBUF", __FUNCTION__); + return NULL; + } + + /* signal handler to know when data is ready to be read() */ - memset(&act, 0, sizeof(act)); - sigemptyset(&act.sa_mask); - act.sa_handler = catchsignal; - sigaction(SIGUSR2, &act, &old); + memset(&act, 0, sizeof(act)); + sigemptyset(&act.sa_mask); + act.sa_handler = catchsignal; + sigaction(SIGUSR2, &act, &old); - dummy = SIGUSR2; - - //viddev->capture_method = METEOR_CAP_CONTINOUS; - //viddev->capture_method = METEOR_CAP_SINGLE; - - if ((viddev->capture_method == METEOR_CAP_CONTINOUS) && (ioctl(dev_bktr, METEORSSIGNAL, &dummy) < 0)) { - motion_log(LOG_ERR, 1, "%s: METEORSSIGNAL", __FUNCTION__); - motion_log(LOG_INFO, 0 , "%s: METEORSSIGNAL", __FUNCTION__); - viddev->capture_method = METEOR_CAP_SINGLE; - if (ioctl(dev_bktr, METEORCAPTUR, &viddev->capture_method) < 0){ - motion_log(LOG_ERR, 1, "%s: METEORCAPTUR using single method " - "Error capturing", __FUNCTION__); - motion_log(LOG_INFO, 0, "%s: METEORCAPTUR using single method " - "Error capturing", __FUNCTION__); - } - } else { - if (ioctl(dev_bktr, METEORCAPTUR, &viddev->capture_method) < 0) { - viddev->capture_method = METEOR_CAP_SINGLE; - if (ioctl(dev_bktr, METEORCAPTUR, &viddev->capture_method) < 0){ - motion_log(LOG_ERR, 1, "%s: METEORCAPTUR using single method " - "Error capturing", __FUNCTION__); - motion_log(LOG_INFO, 0, "%s: METEORCAPTUR using single method " - "Error capturing", __FUNCTION__); - } - } - } + dummy = SIGUSR2; + + //viddev->capture_method = METEOR_CAP_CONTINOUS; + //viddev->capture_method = METEOR_CAP_SINGLE; + + if ((viddev->capture_method == METEOR_CAP_CONTINOUS) && (ioctl(dev_bktr, METEORSSIGNAL, &dummy) < 0)) { + motion_log(LOG_ERR, 1, "%s: METEORSSIGNAL", __FUNCTION__); + motion_log(LOG_INFO, 0 , "%s: METEORSSIGNAL", __FUNCTION__); + viddev->capture_method = METEOR_CAP_SINGLE; + if (ioctl(dev_bktr, METEORCAPTUR, &viddev->capture_method) < 0) { + motion_log(LOG_ERR, 1, "%s: METEORCAPTUR using single method " + "Error capturing", __FUNCTION__); + motion_log(LOG_INFO, 0, "%s: METEORCAPTUR using single method " + "Error capturing", __FUNCTION__); + } + } else { + if (ioctl(dev_bktr, METEORCAPTUR, &viddev->capture_method) < 0) { + viddev->capture_method = METEOR_CAP_SINGLE; + if (ioctl(dev_bktr, METEORCAPTUR, &viddev->capture_method) < 0) { + motion_log(LOG_ERR, 1, "%s: METEORCAPTUR using single method " + "Error capturing", __FUNCTION__); + motion_log(LOG_INFO, 0, "%s: METEORCAPTUR using single method " + "Error capturing", __FUNCTION__); + } + } + } - if (viddev->capture_method == METEOR_CAP_CONTINOUS) - motion_log(LOG_INFO, 0, "%s: METEORCAPTUR METEOR_CAP_CONTINOUS", __FUNCTION__); - else - motion_log(LOG_INFO, 0, "%s: METEORCAPTUR METEOR_CAP_SINGLE", __FUNCTION__); - - // settle , sleep(1) replaced - SLEEP(1, 0); - - /* FIXME*/ - switch (viddev->v4l_fmt) { - case VIDEO_PALETTE_YUV420P: - viddev->v4l_bufsize = (width * height * 3) / 2; - break; - case VIDEO_PALETTE_YUV422: - viddev->v4l_bufsize = (width * height * 2); - break; - case VIDEO_PALETTE_RGB24: - viddev->v4l_bufsize = (width * height * 3); - break; - case VIDEO_PALETTE_GREY: - viddev->v4l_bufsize = width * height; - break; - } - - motion_log(LOG_INFO, 0, "HUE [%d]", get_hue(dev_bktr, &dummy)); - motion_log(LOG_INFO, 0, "SATURATION [%d]", get_saturation(dev_bktr, &dummy)); - motion_log(LOG_INFO, 0, "BRIGHTNESS [%d]", get_brightness(dev_bktr, &dummy)); - motion_log(LOG_INFO, 0, "CONTRAST [%d]", get_contrast(dev_bktr, &dummy)); - - return map; + if (viddev->capture_method == METEOR_CAP_CONTINOUS) + motion_log(LOG_INFO, 0, "%s: METEORCAPTUR METEOR_CAP_CONTINOUS", __FUNCTION__); + else + motion_log(LOG_INFO, 0, "%s: METEORCAPTUR METEOR_CAP_SINGLE", __FUNCTION__); + + // settle , sleep(1) replaced + SLEEP(1, 0); + + /* FIXME*/ + switch (viddev->v4l_fmt) { + case VIDEO_PALETTE_YUV420P: + viddev->v4l_bufsize = (width * height * 3) / 2; + break; + case VIDEO_PALETTE_YUV422: + viddev->v4l_bufsize = (width * height * 2); + break; + case VIDEO_PALETTE_RGB24: + viddev->v4l_bufsize = (width * height * 3); + break; + case VIDEO_PALETTE_GREY: + viddev->v4l_bufsize = width * height; + break; + } + + motion_log(LOG_INFO, 0, "HUE [%d]", get_hue(dev_bktr, &dummy)); + motion_log(LOG_INFO, 0, "SATURATION [%d]", get_saturation(dev_bktr, &dummy)); + motion_log(LOG_INFO, 0, "BRIGHTNESS [%d]", get_brightness(dev_bktr, &dummy)); + motion_log(LOG_INFO, 0, "CONTRAST [%d]", get_contrast(dev_bktr, &dummy)); + + return map; } @@ -793,56 +799,56 @@ static unsigned char *v4l_start(struct video_dev *viddev, int width, int height, */ static int v4l_next(struct video_dev *viddev, unsigned char *map, int width, int height) { - int dev_bktr = viddev->fd_bktr; - unsigned char *cap_map = NULL; - int single = METEOR_CAP_SINGLE; - sigset_t set, old; - - - /* ONLY MMAP method is used to Capture */ - - /* Allocate a new mmap buffer */ - /* Block signals during IOCTL */ - sigemptyset (&set); - sigaddset (&set, SIGCHLD); - sigaddset (&set, SIGALRM); - sigaddset (&set, SIGUSR1); - sigaddset (&set, SIGTERM); - sigaddset (&set, SIGHUP); - pthread_sigmask(SIG_BLOCK, &set, &old); - cap_map = viddev->v4l_buffers[viddev->v4l_curbuffer]; - - viddev->v4l_curbuffer++; - if (viddev->v4l_curbuffer >= viddev->v4l_maxbuffer) - viddev->v4l_curbuffer = 0; - - /* capture */ - - if (viddev->capture_method == METEOR_CAP_CONTINOUS) { - if (bktr_frame_waiting) - bktr_frame_waiting = 0; - - } else if (ioctl(dev_bktr, METEORCAPTUR, &single) < 0) { - motion_log(LOG_ERR, 1, "%s: Error capturing using single method", __FUNCTION__); - sigprocmask(SIG_UNBLOCK, &old, NULL); - return (-1); - } - - /*undo the signal blocking*/ - pthread_sigmask(SIG_UNBLOCK, &old, NULL); - - switch (viddev->v4l_fmt) { - case VIDEO_PALETTE_RGB24: - rgb24toyuv420p(map, cap_map, width, height); - break; - case VIDEO_PALETTE_YUV422: - yuv422to420p(map, cap_map, width, height); - break; - default: - memcpy(map, cap_map, viddev->v4l_bufsize); - } - - return 0; + int dev_bktr = viddev->fd_bktr; + unsigned char *cap_map = NULL; + int single = METEOR_CAP_SINGLE; + sigset_t set, old; + + + /* ONLY MMAP method is used to Capture */ + + /* Allocate a new mmap buffer */ + /* Block signals during IOCTL */ + sigemptyset (&set); + sigaddset (&set, SIGCHLD); + sigaddset (&set, SIGALRM); + sigaddset (&set, SIGUSR1); + sigaddset (&set, SIGTERM); + sigaddset (&set, SIGHUP); + pthread_sigmask(SIG_BLOCK, &set, &old); + cap_map = viddev->v4l_buffers[viddev->v4l_curbuffer]; + + viddev->v4l_curbuffer++; + if (viddev->v4l_curbuffer >= viddev->v4l_maxbuffer) + viddev->v4l_curbuffer = 0; + + /* capture */ + + if (viddev->capture_method == METEOR_CAP_CONTINOUS) { + if (bktr_frame_waiting) + bktr_frame_waiting = 0; + + } else if (ioctl(dev_bktr, METEORCAPTUR, &single) < 0) { + motion_log(LOG_ERR, 1, "%s: Error capturing using single method", __FUNCTION__); + sigprocmask(SIG_UNBLOCK, &old, NULL); + return (-1); + } + + /*undo the signal blocking*/ + pthread_sigmask(SIG_UNBLOCK, &old, NULL); + + switch (viddev->v4l_fmt) { + case VIDEO_PALETTE_RGB24: + rgb24toyuv420p(map, cap_map, width, height); + break; + case VIDEO_PALETTE_YUV422: + yuv422to420p(map, cap_map, width, height); + break; + default: + memcpy(map, cap_map, viddev->v4l_bufsize); + } + + return 0; } @@ -852,54 +858,54 @@ static void v4l_set_input(struct context *cnt, struct video_dev *viddev, unsigne unsigned short input, unsigned short norm, int skip, unsigned long freq) { - if (input != viddev->input || norm != viddev->norm || freq != viddev->freq) { - int dummy; - unsigned long frequnits = freq; - - - if ((dummy = set_input(viddev, input)) == -1) - return; - - viddev->input = dummy; - - if ((dummy = set_input_format(viddev, norm)) == -1) - return; - - viddev->norm = dummy; - - if ((viddev->tuner_device != NULL) && ( viddev->input == IN_TV ) && (frequnits > 0)) { - if (set_freq(viddev, freq) == -1) - return; - } - - // FIXME - /* - if ( setup_pixelformat(viddev) == -1) { - motion_log(LOG_ERR, 1, "ioctl (VIDIOCSFREQ)"); - return - } - */ - - /* - if (set_geometry(viddev, width, height) == -1) - return; - */ - - v4l_picture_controls(cnt, viddev); - - viddev->freq = freq; - - /* skip a few frames if needed */ - for (dummy = 0; dummy < skip; dummy++) - v4l_next(viddev, map, width, height); - }else{ - /* No round robin - we only adjust picture controls */ - v4l_picture_controls(cnt, viddev); - } + if (input != viddev->input || norm != viddev->norm || freq != viddev->freq) { + int dummy; + unsigned long frequnits = freq; + + + if ((dummy = set_input(viddev, input)) == -1) + return; + + viddev->input = dummy; + + if ((dummy = set_input_format(viddev, norm)) == -1) + return; + + viddev->norm = dummy; + + if ((viddev->tuner_device != NULL) && (viddev->input == IN_TV) && (frequnits > 0)) { + if (set_freq(viddev, freq) == -1) + return; + } + + // FIXME + /* + if (setup_pixelformat(viddev) == -1) { + motion_log(LOG_ERR, 1, "ioctl (VIDIOCSFREQ)"); + return + } + */ + + /* + if (set_geometry(viddev, width, height) == -1) + return; + */ + + v4l_picture_controls(cnt, viddev); + + viddev->freq = freq; + + /* skip a few frames if needed */ + for (dummy = 0; dummy < skip; dummy++) + v4l_next(viddev, map, width, height); + } else { + /* No round robin - we only adjust picture controls */ + v4l_picture_controls(cnt, viddev); + } } /***************************************************************************** - Wrappers calling the current capture routines + Wrappers calling the current capture routines *****************************************************************************/ /* @@ -932,7 +938,7 @@ static struct video_dev *viddevs = NULL; */ void vid_init(void) { - pthread_mutex_init(&vid_mutex, NULL); + pthread_mutex_init(&vid_mutex, NULL); } /** @@ -942,7 +948,7 @@ void vid_init(void) */ void vid_cleanup(void) { - pthread_mutex_destroy(&vid_mutex); + pthread_mutex_destroy(&vid_mutex); } #endif /*WITHOUT_V4L*/ @@ -956,79 +962,83 @@ void vid_cleanup(void) void vid_close(struct context *cnt) { #ifndef WITHOUT_V4L - struct video_dev *dev = viddevs; - struct video_dev *prev = NULL; + struct video_dev *dev = viddevs; + struct video_dev *prev = NULL; #endif - /* Cleanup the netcam part */ - if (cnt->netcam) { - netcam_cleanup(cnt->netcam, 0); - cnt->netcam = NULL; - return; - } + /* Cleanup the netcam part */ + if (cnt->netcam) { + netcam_cleanup(cnt->netcam, 0); + cnt->netcam = NULL; + return; + } #ifndef WITHOUT_V4L - /* Cleanup the v4l part */ - pthread_mutex_lock(&vid_mutex); - while (dev) { - if (dev->fd_bktr == cnt->video_dev) - break; - prev = dev; - dev = dev->next; - } - pthread_mutex_unlock(&vid_mutex); + /* Cleanup the v4l part */ + pthread_mutex_lock(&vid_mutex); + + while (dev) { + if (dev->fd_bktr == cnt->video_dev) + break; + prev = dev; + dev = dev->next; + } + + pthread_mutex_unlock(&vid_mutex); - /* Set it as closed in thread context */ - cnt->video_dev = -1; + /* Set it as closed in thread context */ + cnt->video_dev = -1; - if (dev == NULL) { - motion_log(LOG_ERR, 0, "%s: Unable to find video device", __FUNCTION__); - return; + if (dev == NULL) { + motion_log(LOG_ERR, 0, "%s: Unable to find video device", __FUNCTION__); + return; + } + + if (--dev->usage_count == 0) { + motion_log(LOG_INFO, 0, "%s: Closing video device %s", __FUNCTION__, dev->video_device); + if (dev->fd_tuner > 0) + close(dev->fd_tuner); + + if (dev->fd_bktr > 0) { + if (dev->capture_method == METEOR_CAP_CONTINOUS) { + dev->fd_tuner = METEOR_CAP_STOP_CONT; + ioctl(dev->fd_bktr, METEORCAPTUR, &dev->fd_tuner); + } + close(dev->fd_bktr); + dev->fd_tuner = -1; } - if( --dev->usage_count == 0) { - motion_log(LOG_INFO, 0, "%s: Closing video device %s", __FUNCTION__, dev->video_device); - if (dev->fd_tuner > 0) - close(dev->fd_tuner); - - if (dev->fd_bktr > 0) { - if (dev->capture_method == METEOR_CAP_CONTINOUS) { - dev->fd_tuner = METEOR_CAP_STOP_CONT; - ioctl(dev->fd_bktr, METEORCAPTUR, &dev->fd_tuner); - } - close(dev->fd_bktr); - dev->fd_tuner = -1; - } - - - munmap(viddevs->v4l_buffers[0], viddevs->v4l_bufsize); - viddevs->v4l_buffers[0] = MAP_FAILED; - - dev->fd_bktr = -1; - pthread_mutex_lock(&vid_mutex); - /* Remove from list */ - if (prev == NULL) - viddevs = dev->next; - else - prev->next = dev->next; - pthread_mutex_unlock(&vid_mutex); - pthread_mutexattr_destroy(&dev->attr); - pthread_mutex_destroy(&dev->mutex); - free(dev); - } else { - motion_log(LOG_INFO, 0, "%s: Still %d users of video device %s, so we don't close it now", - __FUNCTION__, dev->usage_count, dev->video_device); - /* There is still at least one thread using this device - * If we own it, release it - */ - if (dev->owner == cnt->threadnr) { - dev->frames = 0; - dev->owner = -1; - pthread_mutex_unlock(&dev->mutex); - } + munmap(viddevs->v4l_buffers[0], viddevs->v4l_bufsize); + viddevs->v4l_buffers[0] = MAP_FAILED; + + dev->fd_bktr = -1; + pthread_mutex_lock(&vid_mutex); + + /* Remove from list */ + if (prev == NULL) + viddevs = dev->next; + else + prev->next = dev->next; + + pthread_mutex_unlock(&vid_mutex); + + pthread_mutexattr_destroy(&dev->attr); + pthread_mutex_destroy(&dev->mutex); + free(dev); + } else { + motion_log(LOG_INFO, 0, "%s: Still %d users of video device %s, so we don't close it now", + __FUNCTION__, dev->usage_count, dev->video_device); + /* There is still at least one thread using this device + * If we own it, release it + */ + if (dev->owner == cnt->threadnr) { + dev->frames = 0; + dev->owner = -1; + pthread_mutex_unlock(&dev->mutex); } + } #endif /* WITHOUT_V4L */ } @@ -1036,201 +1046,205 @@ void vid_close(struct context *cnt) int vid_start(struct context *cnt) { - struct config *conf = &cnt->conf; - int fd_bktr = -1; - - if (conf->netcam_url) { - fd_bktr = netcam_start(cnt); - if (fd_bktr < 0) { - netcam_cleanup(cnt->netcam, 1); - cnt->netcam = NULL; - } - } + struct config *conf = &cnt->conf; + int fd_bktr = -1; + + if (conf->netcam_url) { + fd_bktr = netcam_start(cnt); + if (fd_bktr < 0) { + netcam_cleanup(cnt->netcam, 1); + cnt->netcam = NULL; + } + } #ifdef WITHOUT_V4L - else - motion_log(LOG_ERR, 0, "%s: You must setup netcam_url", __FUNCTION__); + else + motion_log(LOG_ERR, 0, "%s: You must setup netcam_url", __FUNCTION__); #else - else{ - struct video_dev *dev; - int fd_tuner = -1; - int width, height, capture_method; - unsigned short input, norm; - unsigned long frequency; - - - motion_log(-1, 0, "%s: [%s]", __FUNCTION__, conf->video_device); - - /* We use width and height from conf in this function. They will be assigned - * to width and height in imgs here, and cap_width and cap_height in - * rotate_data won't be set until in rotate_init. - * Motion requires that width and height are multiples of 16 so we check for this - */ - if (conf->width % 16) { - motion_log(LOG_ERR, 0, - "%s: config image width (%d) is not modulo 16", - __FUNCTION__, conf->width); - return -1; - } - if (conf->height % 16) { - motion_log(LOG_ERR, 0, - "%s: config image height (%d) is not modulo 16", - __FUNCTION__, conf->height); - return -1; - } - width = conf->width; - height = conf->height; - input = conf->input; - norm = conf->norm; - frequency = conf->frequency; - capture_method = METEOR_CAP_CONTINOUS; - - pthread_mutex_lock(&vid_mutex); - - /* Transfer width and height from conf to imgs. The imgs values are the ones - * that is used internally in Motion. That way, setting width and height via - * http remote control won't screw things up. - */ - cnt->imgs.width = width; - cnt->imgs.height = height; - - /* First we walk through the already discovered video devices to see - * if we have already setup the same device before. If this is the case - * the device is a Round Robin device and we set the basic settings - * and return the file descriptor - */ - dev = viddevs; - while (dev) { - if (!strcmp(conf->video_device, dev->video_device)) { - int dummy = METEOR_CAP_STOP_CONT; - dev->usage_count++; - cnt->imgs.type = dev->v4l_fmt; - - if (ioctl(dev->fd_bktr, METEORCAPTUR, &dummy) < 0) { - motion_log(LOG_ERR, 1, "%s Stopping capture", __FUNCTION__); - return -1; - } - - motion_log(-1, 0, "%s Reusing [%s] inputs [%d,%d] Change capture method " - "METEOR_CAP_SINGLE", __FUNCTION__, dev->video_device, - dev->input, conf->input); - - dev->capture_method = METEOR_CAP_SINGLE; - - switch (cnt->imgs.type) { - case VIDEO_PALETTE_GREY: - cnt->imgs.motionsize = width * height; - cnt->imgs.size = width * height; - break; - case VIDEO_PALETTE_RGB24: - case VIDEO_PALETTE_YUV422: - cnt->imgs.type = VIDEO_PALETTE_YUV420P; - case VIDEO_PALETTE_YUV420P: - motion_log(-1, 0, - "%s VIDEO_PALETTE_YUV420P setting imgs.size " - "and imgs.motionsize", __FUNCTION__); - cnt->imgs.motionsize = width * height; - cnt->imgs.size = (width * height * 3) / 2; - break; - } - pthread_mutex_unlock(&vid_mutex); - return dev->fd_bktr; // FIXME return fd_tuner ?! - } - dev = dev->next; - } - - - dev = mymalloc(sizeof(struct video_dev)); - memset(dev, 0, sizeof(struct video_dev)); - - fd_bktr = open(conf->video_device, O_RDWR); - - if (fd_bktr < 0) { - motion_log(LOG_ERR, 1, "%s: open video device %s", __FUNCTION__, conf->video_device); - free(dev); - pthread_mutex_unlock(&vid_mutex); - return -1; - } - - - /* Only open tuner if conf->tuner_device has set , freq and input is 1 */ - if ( (conf->tuner_device != NULL) && (frequency > 0) && ( input == IN_TV )) { - fd_tuner = open(conf->tuner_device, O_RDWR); - if (fd_tuner < 0) { - motion_log(LOG_ERR, 1, "%s: open tuner device %s", - __FUNCTION__, conf->tuner_device); - free(dev); - pthread_mutex_unlock(&vid_mutex); - return -1; - } - } - - pthread_mutexattr_init(&dev->attr); - pthread_mutex_init(&dev->mutex, &dev->attr); - - dev->usage_count = 1; - dev->video_device = conf->video_device; - dev->tuner_device = conf->tuner_device; - dev->fd_bktr = fd_bktr; - dev->fd_tuner = fd_tuner; - dev->input = input; - dev->height = height; - dev->width = width; - dev->freq = frequency; - dev->owner = -1; - dev->capture_method = capture_method; - - /* We set brightness, contrast, saturation and hue = 0 so that they only get - * set if the config is not zero. - */ - - dev->brightness = 0; - dev->contrast = 0; - dev->saturation = 0; - dev->hue = 0; - dev->owner = -1; - - /* default palette */ - dev->v4l_fmt = VIDEO_PALETTE_YUV420P; - dev->v4l_curbuffer = 0; - dev->v4l_maxbuffer = 1; - - if (!v4l_start(dev, width, height, input, norm, frequency)) { - close(dev->fd_bktr); - pthread_mutexattr_destroy(&dev->attr); - pthread_mutex_destroy(&dev->mutex); - free(dev); - - pthread_mutex_unlock(&vid_mutex); - return -1; - } - - cnt->imgs.type = dev->v4l_fmt; - - switch (cnt->imgs.type) { - case VIDEO_PALETTE_GREY: - cnt->imgs.size = width * height; - cnt->imgs.motionsize = width * height; - break; - case VIDEO_PALETTE_RGB24: - case VIDEO_PALETTE_YUV422: - cnt->imgs.type = VIDEO_PALETTE_YUV420P; - case VIDEO_PALETTE_YUV420P: - motion_log(-1, 0, "%s: VIDEO_PALETTE_YUV420P imgs.type", __FUNCTION__); - cnt->imgs.size = (width * height * 3) / 2; - cnt->imgs.motionsize = width * height; - break; - } - - /* Insert into linked list */ - dev->next = viddevs; - viddevs = dev; - - pthread_mutex_unlock(&vid_mutex); - } + else { + struct video_dev *dev; + int fd_tuner = -1; + int width, height, capture_method; + unsigned short input, norm; + unsigned long frequency; + + + motion_log(-1, 0, "%s: [%s]", __FUNCTION__, conf->video_device); + + /* We use width and height from conf in this function. They will be assigned + * to width and height in imgs here, and cap_width and cap_height in + * rotate_data won't be set until in rotate_init. + * Motion requires that width and height are multiples of 16 so we check for this + */ + if (conf->width % 16) { + motion_log(LOG_ERR, 0, + "%s: config image width (%d) is not modulo 16", + __FUNCTION__, conf->width); + return -1; + } + + if (conf->height % 16) { + motion_log(LOG_ERR, 0, + "%s: config image height (%d) is not modulo 16", + __FUNCTION__, conf->height); + return -1; + } + + width = conf->width; + height = conf->height; + input = conf->input; + norm = conf->norm; + frequency = conf->frequency; + capture_method = METEOR_CAP_CONTINOUS; + + pthread_mutex_lock(&vid_mutex); + + /* Transfer width and height from conf to imgs. The imgs values are the ones + * that is used internally in Motion. That way, setting width and height via + * http remote control won't screw things up. + */ + cnt->imgs.width = width; + cnt->imgs.height = height; + + /* First we walk through the already discovered video devices to see + * if we have already setup the same device before. If this is the case + * the device is a Round Robin device and we set the basic settings + * and return the file descriptor + */ + dev = viddevs; + while (dev) { + if (!strcmp(conf->video_device, dev->video_device)) { + int dummy = METEOR_CAP_STOP_CONT; + dev->usage_count++; + cnt->imgs.type = dev->v4l_fmt; + + if (ioctl(dev->fd_bktr, METEORCAPTUR, &dummy) < 0) { + motion_log(LOG_ERR, 1, "%s Stopping capture", __FUNCTION__); + return -1; + } + + motion_log(-1, 0, "%s Reusing [%s] inputs [%d,%d] Change capture method " + "METEOR_CAP_SINGLE", __FUNCTION__, dev->video_device, + dev->input, conf->input); + + dev->capture_method = METEOR_CAP_SINGLE; + + switch (cnt->imgs.type) { + case VIDEO_PALETTE_GREY: + cnt->imgs.motionsize = width * height; + cnt->imgs.size = width * height; + break; + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_YUV422: + cnt->imgs.type = VIDEO_PALETTE_YUV420P; + case VIDEO_PALETTE_YUV420P: + motion_log(-1, 0, + "%s VIDEO_PALETTE_YUV420P setting imgs.size " + "and imgs.motionsize", __FUNCTION__); + cnt->imgs.motionsize = width * height; + cnt->imgs.size = (width * height * 3) / 2; + break; + } + + pthread_mutex_unlock(&vid_mutex); + return dev->fd_bktr; // FIXME return fd_tuner ?! + } + dev = dev->next; + } + + + dev = mymalloc(sizeof(struct video_dev)); + memset(dev, 0, sizeof(struct video_dev)); + + fd_bktr = open(conf->video_device, O_RDWR); + + if (fd_bktr < 0) { + motion_log(LOG_ERR, 1, "%s: open video device %s", __FUNCTION__, conf->video_device); + free(dev); + pthread_mutex_unlock(&vid_mutex); + return -1; + } + + + /* Only open tuner if conf->tuner_device has set , freq and input is 1 */ + if ((conf->tuner_device != NULL) && (frequency > 0) && (input == IN_TV)) { + fd_tuner = open(conf->tuner_device, O_RDWR); + if (fd_tuner < 0) { + motion_log(LOG_ERR, 1, "%s: open tuner device %s", + __FUNCTION__, conf->tuner_device); + free(dev); + pthread_mutex_unlock(&vid_mutex); + return -1; + } + } + + pthread_mutexattr_init(&dev->attr); + pthread_mutex_init(&dev->mutex, &dev->attr); + + dev->usage_count = 1; + dev->video_device = conf->video_device; + dev->tuner_device = conf->tuner_device; + dev->fd_bktr = fd_bktr; + dev->fd_tuner = fd_tuner; + dev->input = input; + dev->height = height; + dev->width = width; + dev->freq = frequency; + dev->owner = -1; + dev->capture_method = capture_method; + + /* + * We set brightness, contrast, saturation and hue = 0 so that they only get + * set if the config is not zero. + */ + + dev->brightness = 0; + dev->contrast = 0; + dev->saturation = 0; + dev->hue = 0; + dev->owner = -1; + + /* default palette */ + dev->v4l_fmt = VIDEO_PALETTE_YUV420P; + dev->v4l_curbuffer = 0; + dev->v4l_maxbuffer = 1; + + if (!v4l_start(dev, width, height, input, norm, frequency)) { + close(dev->fd_bktr); + pthread_mutexattr_destroy(&dev->attr); + pthread_mutex_destroy(&dev->mutex); + free(dev); + + pthread_mutex_unlock(&vid_mutex); + return -1; + } + + cnt->imgs.type = dev->v4l_fmt; + + switch (cnt->imgs.type) { + case VIDEO_PALETTE_GREY: + cnt->imgs.size = width * height; + cnt->imgs.motionsize = width * height; + break; + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_YUV422: + cnt->imgs.type = VIDEO_PALETTE_YUV420P; + case VIDEO_PALETTE_YUV420P: + motion_log(-1, 0, "%s: VIDEO_PALETTE_YUV420P imgs.type", __FUNCTION__); + cnt->imgs.size = (width * height * 3) / 2; + cnt->imgs.motionsize = width * height; + break; + } + + /* Insert into linked list */ + dev->next = viddevs; + viddevs = dev; + + pthread_mutex_unlock(&vid_mutex); + } #endif /* WITHOUT_V4L */ - /* FIXME needed tuner device ?! */ - return fd_bktr; + /* FIXME needed tuner device ?! */ + return fd_bktr; } @@ -1249,64 +1263,64 @@ int vid_start(struct context *cnt) */ int vid_next(struct context *cnt, unsigned char *map) { - struct config *conf = &cnt->conf; - int ret = -1; + struct config *conf = &cnt->conf; + int ret = -1; - if (conf->netcam_url) { - if (cnt->video_dev == -1) - return NETCAM_GENERAL_ERROR; + if (conf->netcam_url) { + if (cnt->video_dev == -1) + return NETCAM_GENERAL_ERROR; - ret = netcam_next(cnt, map); - return ret; - } + ret = netcam_next(cnt, map); + return ret; + } #ifndef WITHOUT_V4L - struct video_dev *dev; - int width, height; - int dev_bktr = cnt->video_dev; - - /* NOTE: Since this is a capture, we need to use capture dimensions. */ - width = cnt->rotate_data.cap_width; - height = cnt->rotate_data.cap_height; - - pthread_mutex_lock(&vid_mutex); - dev = viddevs; - while (dev) { - if (dev->fd_bktr == dev_bktr) - break; - dev = dev->next; - } - - pthread_mutex_unlock(&vid_mutex); - - if (dev == NULL) - return V4L_FATAL_ERROR; - //return -1; - - if (dev->owner != cnt->threadnr) { - pthread_mutex_lock(&dev->mutex); - dev->owner = cnt->threadnr; - dev->frames = conf->roundrobin_frames; - } - - - v4l_set_input(cnt, dev, map, width, height, conf->input, conf->norm, - conf->roundrobin_skip, conf->frequency); - - ret = v4l_next(dev, map, width, height); - - if (--dev->frames <= 0) { - dev->owner = -1; - dev->frames = 0; - pthread_mutex_unlock(&dev->mutex); - } - - if (cnt->rotate_data.degrees > 0) { - /* rotate the image as specified */ - rotate_map(cnt, map); - } - + struct video_dev *dev; + int width, height; + int dev_bktr = cnt->video_dev; + + /* NOTE: Since this is a capture, we need to use capture dimensions. */ + width = cnt->rotate_data.cap_width; + height = cnt->rotate_data.cap_height; + + pthread_mutex_lock(&vid_mutex); + dev = viddevs; + while (dev) { + if (dev->fd_bktr == dev_bktr) + break; + dev = dev->next; + } + + pthread_mutex_unlock(&vid_mutex); + + if (dev == NULL) + return V4L_FATAL_ERROR; + //return -1; + + if (dev->owner != cnt->threadnr) { + pthread_mutex_lock(&dev->mutex); + dev->owner = cnt->threadnr; + dev->frames = conf->roundrobin_frames; + } + + + v4l_set_input(cnt, dev, map, width, height, conf->input, conf->norm, + conf->roundrobin_skip, conf->frequency); + + ret = v4l_next(dev, map, width, height); + + if (--dev->frames <= 0) { + dev->owner = -1; + dev->frames = 0; + pthread_mutex_unlock(&dev->mutex); + } + + /* rotate the image as specified */ + if (cnt->rotate_data.degrees > 0) + rotate_map(cnt, map); + + #endif /*WITHOUT_V4L*/ - return ret; + return ret; } diff --git a/webhttpd.c b/webhttpd.c index 5b42b7b..a67282e 100644 --- a/webhttpd.c +++ b/webhttpd.c @@ -10,7 +10,7 @@ * See also the file 'COPYING'. * */ -#include "webhttpd.h" /* already includes motion.h */ +#include "webhttpd.h" /* already includes motion.h */ #include #include #include @@ -21,267 +21,267 @@ pthread_mutex_t httpd_mutex; int warningkill; // This is a dummy variable use to kill warnings when not checking sscanf and similar functions static const char *ini_template = - "Motion "VERSION"\n" - "\n"; + "Motion "VERSION"\n" + "\n"; static const char *set_template = - "\nMotion "VERSION"\n" - "\n"; + "\nMotion "VERSION"\n" + "\n"; static const char *end_template = - "\n" - "\n"; + "\n" + "\n"; static const char *ok_response = - "HTTP/1.1 200 OK\r\n" - "Server: Motion-httpd/"VERSION"\r\n" - "Connection: close\r\n" - "Max-Age: 0\r\n" - "Expires: 0\r\n" - "Cache-Control: no-cache\r\n" - "Cache-Control: private\r\n" - "Pragma: no-cache\r\n" - "Content-type: text/html\r\n\r\n"; + "HTTP/1.1 200 OK\r\n" + "Server: Motion-httpd/"VERSION"\r\n" + "Connection: close\r\n" + "Max-Age: 0\r\n" + "Expires: 0\r\n" + "Cache-Control: no-cache\r\n" + "Cache-Control: private\r\n" + "Pragma: no-cache\r\n" + "Content-type: text/html\r\n\r\n"; static const char *ok_response_raw = - "HTTP/1.1 200 OK\r\n" - "Server: Motion-httpd/"VERSION"\r\n" - "Connection: close\r\n" - "Max-Age: 0\r\n" - "Expires: 0\r\n" - "Cache-Control: no-cache\r\n" - "Cache-Control: private\r\n" - "Pragma: no-cache\r\n" - "Content-type: text/plain\r\n\r\n"; + "HTTP/1.1 200 OK\r\n" + "Server: Motion-httpd/"VERSION"\r\n" + "Connection: close\r\n" + "Max-Age: 0\r\n" + "Expires: 0\r\n" + "Cache-Control: no-cache\r\n" + "Cache-Control: private\r\n" + "Pragma: no-cache\r\n" + "Content-type: text/plain\r\n\r\n"; static const char *bad_request_response = - "HTTP/1.0 400 Bad Request\r\n" - "Content-type: text/html\r\n\r\n" - "\n" - "\n" - "

Bad Request

\n" - "

The server did not understand your request.

\n" - "\n" - "\n"; + "HTTP/1.0 400 Bad Request\r\n" + "Content-type: text/html\r\n\r\n" + "\n" + "\n" + "

Bad Request

\n" + "

The server did not understand your request.

\n" + "\n" + "\n"; static const char *bad_request_response_raw = - "HTTP/1.0 400 Bad Request\r\n" - "Content-type: text/plain\r\n\r\n" - "Bad Request"; + "HTTP/1.0 400 Bad Request\r\n" + "Content-type: text/plain\r\n\r\n" + "Bad Request"; static const char *not_found_response_template = - "HTTP/1.0 404 Not Found\r\n" - "Content-type: text/html\r\n\r\n" - "\n" - "\n" - "

Not Found

\n" - "

The requested URL was not found on the server.

\n" - "\n" - "\n"; + "HTTP/1.0 404 Not Found\r\n" + "Content-type: text/html\r\n\r\n" + "\n" + "\n" + "

Not Found

\n" + "

The requested URL was not found on the server.

\n" + "\n" + "\n"; static const char *not_found_response_template_raw = - "HTTP/1.0 404 Not Found\r\n" - "Content-type: text/plain\r\n\r\n" - "Not Found"; + "HTTP/1.0 404 Not Found\r\n" + "Content-type: text/plain\r\n\r\n" + "Not Found"; static const char *not_found_response_valid = - "HTTP/1.0 404 Not Valid\r\n" - "Content-type: text/html\r\n\r\n" - "\n" - "\n" - "

Not Valid

\n" - "

The requested URL is not valid.

\n" - "\n" - "\n"; + "HTTP/1.0 404 Not Valid\r\n" + "Content-type: text/html\r\n\r\n" + "\n" + "\n" + "

Not Valid

\n" + "

The requested URL is not valid.

\n" + "\n" + "\n"; static const char *not_found_response_valid_raw = - "HTTP/1.0 404 Not Valid\r\n" - "Content-type: text/plain\r\n\r\n" - "The requested URL is not valid."; + "HTTP/1.0 404 Not Valid\r\n" + "Content-type: text/plain\r\n\r\n" + "The requested URL is not valid."; static const char *not_valid_syntax = - "HTTP/1.0 404 Not Valid Syntax\r\n" - "Content-type: text/html\r\n\r\n" - "\n" - "\n" - "

Not Valid Syntax

\n" - "\n" - "\n"; + "HTTP/1.0 404 Not Valid Syntax\r\n" + "Content-type: text/html\r\n\r\n" + "\n" + "\n" + "

Not Valid Syntax

\n" + "\n" + "\n"; static const char *not_valid_syntax_raw = - "HTTP/1.0 404 Not Valid Syntax\r\n" - "Content-type: text/plain\r\n\r\n" - "Not Valid Syntax\n"; + "HTTP/1.0 404 Not Valid Syntax\r\n" + "Content-type: text/plain\r\n\r\n" + "Not Valid Syntax\n"; static const char *not_track = - "HTTP/1.0 200 OK\r\n" - "Content-type: text/html\r\n\r\n" - "\n" - "\n" - "

Tracking Not Enabled

\n"; + "HTTP/1.0 200 OK\r\n" + "Content-type: text/html\r\n\r\n" + "\n" + "\n" + "

Tracking Not Enabled

\n"; static const char *not_track_raw = - "HTTP/1.0 200 OK\r\n" - "Content-type: text/plain\r\n\r\n" - "Tracking Not Enabled"; + "HTTP/1.0 200 OK\r\n" + "Content-type: text/plain\r\n\r\n" + "Tracking Not Enabled"; static const char *track_error = - "HTTP/1.0 200 OK\r\n" - "Content-type: text/html\r\n\r\n" - "\n" - "\n" - "

Track Error

\n"; + "HTTP/1.0 200 OK\r\n" + "Content-type: text/html\r\n\r\n" + "\n" + "\n" + "

Track Error

\n"; static const char *track_error_raw = - "HTTP/1.0 200 OK\r\n" - "Content-type: text/plain\r\n\r\n" - "Track Error"; + "HTTP/1.0 200 OK\r\n" + "Content-type: text/plain\r\n\r\n" + "Track Error"; static const char *error_value = - "HTTP/1.0 200 OK\r\n" - "Content-type: text/html\r\n\r\n" - "\n" - "\n" - "

Value Error

\n"; + "HTTP/1.0 200 OK\r\n" + "Content-type: text/html\r\n\r\n" + "\n" + "\n" + "

Value Error

\n"; static const char *error_value_raw = - "HTTP/1.0 200 OK\r\n" - "Content-type: text/plain\r\n\r\n" - "Value Error"; - + "HTTP/1.0 200 OK\r\n" + "Content-type: text/plain\r\n\r\n" + "Value Error"; + static const char *not_found_response_valid_command = - "HTTP/1.0 404 Not Valid Command\r\n" - "Content-type: text/html\r\n\r\n" - "\n" - "\n" - "

Not Valid Command

\n" - "

The requested URL is not valid Command.

\n" - "\n" - "\n"; + "HTTP/1.0 404 Not Valid Command\r\n" + "Content-type: text/html\r\n\r\n" + "\n" + "\n" + "

Not Valid Command

\n" + "

The requested URL is not valid Command.

\n" + "\n" + "\n"; static const char *not_found_response_valid_command_raw = - "HTTP/1.0 404 Not Valid Command\r\n" - "Content-type: text/plain\n\n" - "Not Valid Command\n"; + "HTTP/1.0 404 Not Valid Command\r\n" + "Content-type: text/plain\n\n" + "Not Valid Command\n"; static const char *bad_method_response_template = - "HTTP/1.0 501 Method Not Implemented\r\n" - "Content-type: text/html\r\n\r\n" - "\n" - "\n" - "

Method Not Implemented

\n" - "

The method is not implemented by this server.

\n" - "\n" - "\n"; + "HTTP/1.0 501 Method Not Implemented\r\n" + "Content-type: text/html\r\n\r\n" + "\n" + "\n" + "

Method Not Implemented

\n" + "

The method is not implemented by this server.

\n" + "\n" + "\n"; static const char *bad_method_response_template_raw = - "HTTP/1.0 501 Method Not Implemented\r\n" - "Content-type: text/plain\r\n\r\n" - "Method Not Implemented\n"; + "HTTP/1.0 501 Method Not Implemented\r\n" + "Content-type: text/plain\r\n\r\n" + "Method Not Implemented\n"; static const char *request_auth_response_template= - "HTTP/1.0 401 Authorization Required\r\n" - "WWW-Authenticate: Basic realm=\"Motion Security Access\"\r\n"; + "HTTP/1.0 401 Authorization Required\r\n" + "WWW-Authenticate: Basic realm=\"Motion Security Access\"\r\n"; static ssize_t write_nonblock(int fd, const void *buf, size_t size) { - ssize_t nwrite = -1; - struct timeval tm; - fd_set fds; - - tm.tv_sec = 1; /* Timeout in seconds */ - tm.tv_usec = 0; - FD_ZERO(&fds); - FD_SET(fd, &fds); - - if ( select (fd + 1, NULL, &fds, NULL, &tm) > 0) { - if(FD_ISSET(fd, &fds)) { - if ( (nwrite = write(fd , buf, size)) < 0 ) { - if ( errno != EWOULDBLOCK ) - return -1; - } - } - } - - return nwrite; + ssize_t nwrite = -1; + struct timeval tm; + fd_set fds; + + tm.tv_sec = 1; /* Timeout in seconds */ + tm.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(fd, &fds); + + if (select(fd + 1, NULL, &fds, NULL, &tm) > 0) { + if (FD_ISSET(fd, &fds)) { + if ((nwrite = write(fd , buf, size)) < 0) { + if (errno != EWOULDBLOCK) + return -1; + } + } + } + + return nwrite; } static ssize_t read_nonblock(int fd ,void *buf, ssize_t size) { - ssize_t nread = -1; - struct timeval tm; - fd_set fds; - - tm.tv_sec = 1; /* Timeout in seconds */ - tm.tv_usec = 0; - FD_ZERO(&fds); - FD_SET(fd, &fds); - - if ( select (fd + 1, &fds, NULL, NULL, &tm) > 0) { - if(FD_ISSET(fd, &fds)) { - if ( (nread = read(fd , buf, size)) < 0 ) { - if ( errno != EWOULDBLOCK ) - return -1; - } - } - } - - return nread; + ssize_t nread = -1; + struct timeval tm; + fd_set fds; + + tm.tv_sec = 1; /* Timeout in seconds */ + tm.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(fd, &fds); + + if (select(fd + 1, &fds, NULL, NULL, &tm) > 0) { + if (FD_ISSET(fd, &fds)) { + if ((nread = read(fd , buf, size)) < 0) { + if (errno != EWOULDBLOCK) + return -1; + } + } + } + + return nread; } static void send_template_ini_client(int client_socket, const char *template) { - ssize_t nwrite = 0; - nwrite = write_nonblock(client_socket, ok_response, strlen (ok_response)); - nwrite += write_nonblock(client_socket, template, strlen(template)); - if (nwrite != (ssize_t)(strlen(ok_response) + strlen(template))) - motion_log(LOG_ERR, 1, "%s: failure write", __FUNCTION__); + ssize_t nwrite = 0; + nwrite = write_nonblock(client_socket, ok_response, strlen (ok_response)); + nwrite += write_nonblock(client_socket, template, strlen(template)); + if (nwrite != (ssize_t)(strlen(ok_response) + strlen(template))) + motion_log(LOG_ERR, 1, "%s: failure write", __FUNCTION__); } static void send_template_ini_client_raw(int client_socket) { - ssize_t nwrite = 0; - nwrite = write_nonblock(client_socket, ok_response_raw, strlen (ok_response_raw)); - if (nwrite != (ssize_t)strlen(ok_response_raw)) - motion_log(LOG_ERR, 1, "%s: failure write", __FUNCTION__); + ssize_t nwrite = 0; + nwrite = write_nonblock(client_socket, ok_response_raw, strlen (ok_response_raw)); + if (nwrite != (ssize_t)strlen(ok_response_raw)) + motion_log(LOG_ERR, 1, "%s: failure write", __FUNCTION__); } static void send_template(int client_socket, char *res) { - ssize_t nwrite = 0; - nwrite = write_nonblock(client_socket, res, strlen(res)); - if ( nwrite != (ssize_t)strlen(res)) - motion_log(LOG_ERR, 1, "%s: failure write", __FUNCTION__); + ssize_t nwrite = 0; + nwrite = write_nonblock(client_socket, res, strlen(res)); + if (nwrite != (ssize_t)strlen(res)) + motion_log(LOG_ERR, 1, "%s: failure write", __FUNCTION__); } static void send_template_raw(int client_socket, char *res) { - ssize_t nwrite = 0; - nwrite = write_nonblock(client_socket, res, strlen(res)); + ssize_t nwrite = 0; + nwrite = write_nonblock(client_socket, res, strlen(res)); } static void send_template_end_client(int client_socket) { - ssize_t nwrite = 0; - nwrite = write_nonblock(client_socket, end_template, strlen(end_template)); + ssize_t nwrite = 0; + nwrite = write_nonblock(client_socket, end_template, strlen(end_template)); } static void response_client(int client_socket, const char *template, char *back) { - ssize_t nwrite = 0; - nwrite = write_nonblock(client_socket, template, strlen(template)); - if (back != NULL) { - send_template(client_socket, back); - send_template_end_client(client_socket); - } + ssize_t nwrite = 0; + nwrite = write_nonblock(client_socket, template, strlen(template)); + if (back != NULL) { + send_template(client_socket, back); + send_template_end_client(client_socket); + } } @@ -320,38 +320,41 @@ static char *replace(const char *str, const char *old, const char *new) */ static void url_decode(char *urlencoded, size_t length) { - char *data = urlencoded; - char *urldecoded = urlencoded; - - while (length > 0) { - if (*data == '%') { - char c[3]; - int i; - data++; - length--; - c[0] = *data++; - length--; - c[1] = *data; - c[2] = 0; - warningkill = sscanf(c, "%x", &i); - if (i < 128) - *urldecoded++ = (char)i; - else { - *urldecoded++ = '%'; - *urldecoded++ = c[0]; - *urldecoded++ = c[1]; - } - } else if (*data == '+') { - *urldecoded++ = ' '; - - } - else { - *urldecoded++ = *data; - } - data++; - length--; - } - *urldecoded = '\0'; + char *data = urlencoded; + char *urldecoded = urlencoded; + + while (length > 0) { + if (*data == '%') { + char c[3]; + int i; + data++; + length--; + c[0] = *data++; + length--; + c[1] = *data; + c[2] = 0; + + warningkill = sscanf(c, "%x", &i); + + if (i < 128) { + *urldecoded++ = (char)i; + } else { + *urldecoded++ = '%'; + *urldecoded++ = c[0]; + *urldecoded++ = c[1]; + } + + } else if (*data == '+') { + *urldecoded++ = ' '; + + } else { + *urldecoded++ = *data; + } + + data++; + length--; + } + *urldecoded = '\0'; } @@ -362,529 +365,529 @@ static void url_decode(char *urlencoded, size_t length) */ static unsigned short int config(char *pointer, char *res, unsigned short int length_uri, - unsigned short int thread, int client_socket, void *userdata) + unsigned short int thread, int client_socket, void *userdata) { - char question='\0'; - char command[256] = {'\0'}; - unsigned short int i; - struct context **cnt = userdata; - - warningkill = sscanf(pointer, "%255[a-z]%c", command , &question); - if (!strcmp(command, "list")) { - pointer = pointer + 4; - length_uri = length_uri - 4; - if (length_uri == 0) { - const char *value = NULL; - char *retval = NULL; - /*call list*/ - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\nThread %hu\n
    ", thread, thread); - send_template(client_socket, res); - - for (i=0; config_params[i].param_name != NULL; i++) { - - if ((thread != 0) && (config_params[i].main_thread)) - continue; - - value = config_params[i].print(cnt, NULL, i, thread); - - if (value == NULL) { - retval = NULL; - - /* Only get the thread value for main thread */ - if (thread == 0) - config_params[i].print(cnt, &retval, i, thread); - - /* thread value*/ - - if (retval) { - - if (!strcmp(retval,"")) { - free(retval); - retval = mystrdup("No threads"); - } else { - char *temp = retval; - size_t retval_miss = 0; - size_t retval_len = strlen(retval); - unsigned short int ind = 0; - char thread_strings[1024] = {'\0'}; - - while (retval_miss != retval_len) { - while (*temp != '\n') { - thread_strings[ind++] = *temp; - retval_miss++; - temp++; - } - temp++; - thread_strings[ind++] = '<'; - thread_strings[ind++] = 'b'; - thread_strings[ind++] = 'r'; - thread_strings[ind++] = '>'; - retval_miss++; - } - free(retval); - retval = NULL; - retval = mystrdup(thread_strings); - } - - sprintf(res, "
  • %s = %s
  • \n", thread, - config_params[i].param_name, config_params[i].param_name, retval); - free(retval); - } else if (thread != 0) { - /* get the value from main thread for the rest of threads */ - value = config_params[i].print(cnt, NULL, i, 0); - - sprintf(res, "
  • %s = %s
  • \n", thread, - config_params[i].param_name, config_params[i].param_name, - value ? value : "(not defined)"); - } else { - sprintf(res, "
  • %s = %s
  • \n", thread, - config_params[i].param_name, config_params[i].param_name, - "(not defined)"); - } - - } else { - sprintf(res, "
  • %s = %s
  • \n", thread, - config_params[i].param_name, config_params[i].param_name, value); - } - send_template(client_socket, res); - } - - sprintf(res, "
<– back", thread); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - for (i=0; config_params[i].param_name != NULL; i++) { - value = config_params[i].print(cnt, NULL, i, thread); - if (value == NULL) - value = config_params[i].print(cnt, NULL, i, 0); - sprintf(res, "%s = %s\n", config_params[i].param_name, value); - send_template_raw(client_socket, res); - } - } - } else { - /*error*/ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else if (!strcmp(command, "set")) { - /* set?param_name=value */ - pointer = pointer + 3; - length_uri = length_uri - 3; - if ((length_uri != 0) && (question == '?')) { - pointer++; - length_uri--; - warningkill = sscanf(pointer,"%255[-0-9a-z_]%c", command, &question); - /*check command , question == '=' length_uri too*/ - if ((question == '=') && (command[0] != '\0')) { - length_uri = length_uri - strlen(command) - 1; - pointer = pointer + strlen(command) + 1; - /* check if command exists and type of command and not end of URI */ - i=0; - while (config_params[i].param_name != NULL) { - if ((thread != 0) && (config_params[i].main_thread)) { - i++; - continue; - } - - if (!strcasecmp(command, config_params[i].param_name)) - break; - i++; - } - - if (config_params[i].param_name) { - if (length_uri > 0) { - char Value[1024] = {'\0'}; - warningkill = sscanf(pointer,"%1023s", Value); - length_uri = length_uri - strlen(Value); - if ( (length_uri == 0) && (strlen(Value) > 0) ) { - /* FIXME need to assure that is a valid value */ - url_decode(Value, strlen(Value)); - conf_cmdparse(cnt + thread, config_params[i].param_name, Value); - if (cnt[0]->conf.webcontrol_html_output) { - sprintf(res, - "<– back" - "

\nThread %hu\n" - "
  • %s = %s" - "
Done", - thread, thread, thread, config_params[i].param_name, - config_params[i].param_name, Value); - - send_template_ini_client(client_socket, ini_template); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "%s = %s\nDone\n", config_params[i].param_name, Value); - send_template_raw(client_socket, res); - } - } else { - /*error*/ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_valid_syntax, NULL); - else - response_client(client_socket, not_valid_syntax_raw, NULL); - } - } else { - char *type = NULL; - type = mystrdup(config_type(&config_params[i])); - - if (!strcmp(type, "string")) { - char *value = NULL; - conf_cmdparse(cnt+thread, config_params[i].param_name, value); - free(type); - type = mystrdup("(null)"); - } else if (!strcmp(type, "int")) { - free(type); - type = mystrdup("0"); - conf_cmdparse(cnt+thread, config_params[i].param_name, type); - } else if (!strcmp(type, "short")) { - free(type); - type = mystrdup("0"); - conf_cmdparse(cnt+thread, config_params[i].param_name, type); - } else if (!strcmp(type, "bool")) { - free(type); - type = mystrdup("off"); - conf_cmdparse(cnt+thread, config_params[i].param_name, type); - } else { - free(type); - type = mystrdup("unknown"); - } - - if (cnt[0]->conf.webcontrol_html_output) { - sprintf(res, - "<– back

\n" - "Thread %hu\n
  • %s" - "= %s

Done", thread, thread, thread, - config_params[i].param_name, config_params[i].param_name, type); - - send_template_ini_client(client_socket, ini_template); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "%s = %s\nDone\n", config_params[i].param_name, type); - send_template_raw(client_socket, res); - } - free(type); - - } - } else { - /*error*/ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else { - /* Show param_name dialogue only for html output */ - if ( (cnt[0]->conf.webcontrol_html_output) && (command[0] != '\0') && - (((length_uri = length_uri - strlen(command)) == 0 )) ) { - i=0; - while (config_params[i].param_name != NULL) { - if ((thread != 0) && (config_params[i].main_thread)) { - i++; - continue; - } - - if (!strcasecmp(command, config_params[i].param_name)) - break; - i++; - } - /* param_name exists */ - if (config_params[i].param_name) { - const char *value = NULL; - char *text_help = NULL; - char *sharp = NULL; - - value = config_params[i].print(cnt, NULL, i, thread); - - sharp = strstr(config_params[i].param_help, "#\n\n#"); - if (sharp == NULL) - sharp = strstr(config_params[i].param_help, "#"); - sharp++; - - text_help = replace(sharp, "\n#", "
"); - - send_template_ini_client(client_socket, ini_template); - if (!strcmp ("bool", config_type(&config_params[i])) ) { - char option[80] = {'\0'}; - - if ((value == NULL) && (thread != 0)) - value = config_params[i].print(cnt, NULL, i, 0); - - if (!strcmp ("on", value)) - sprintf(option, "\n" - "\n"); - else - sprintf(option, "\n" - "\n"); - - sprintf(res, "<– back

\n" - "Thread %hu\n" - "
\n" - "%s \n" - "    " - "[help]" - "
\n
%s", thread, thread, - config_params[i].param_name, config_params[i].param_name, - option, TWIKI_URL, config_params[i].param_name, text_help); - }else{ - - if (value == NULL) { - if (thread != 0) - /* get the value from main thread for the rest of threads */ - value = config_params[i].print(cnt, NULL, i, 0); - if (value == NULL) value = ""; - } - sprintf(res, "<– back

\n" - "Thread %hu\n
\n" - "%s \n" - "\n" - "    " - "[help]" - "
\n
%s", thread, thread, - config_params[i].param_name, config_params[i].param_name, - value, TWIKI_URL, config_params[i].param_name, text_help); - } - - send_template(client_socket, res); - send_template_end_client(client_socket); - free(text_help); - } else { - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else { - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } - } else if (length_uri == 0) { - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, set_template); - sprintf(res, "<– back

\nThread %hu\n" - "
\n\n
\n" - "
\n" - "\n" - "\n" - "
\n"); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "set needs param_name=value\n"); - send_template_raw(client_socket, res); - } - } else { - /*error*/ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else if (!strcmp(command, "get")) { - /* get?query=param_name */ - pointer = pointer + 3; - length_uri = length_uri - 3; - - if ((length_uri > 7) && (question == '?')) { - /* 8 -> query=param_name FIXME minimum length param_name */ - pointer++; - length_uri--; - warningkill = sscanf(pointer,"%255[-0-9a-z]%c", command, &question); - - if ( (question == '=') && (!strcmp(command, "query")) ) { - pointer = pointer + 6; - length_uri = length_uri - 6; - warningkill = sscanf(pointer, "%255[-0-9a-z_]", command); - /*check if command exist, length_uri too*/ - length_uri = length_uri-strlen(command); - - if (length_uri == 0) { - const char *value = NULL; - i = 0; - while (config_params[i].param_name != NULL) { - if ((thread != 0) && (config_params[i].main_thread)) { - i++; - continue; - } - - if (!strcasecmp(command, config_params[i].param_name)) - break; - i++; - } - /* FIXME bool values or commented values maybe that should be - solved with config_type */ - - if (config_params[i].param_name) { - const char *type = NULL; - type = config_type(&config_params[i]); - if (!strcmp(type, "unknown")) { - /*error doesn't exists this param_name */ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - return 1; - } else { - char *text_help = NULL; - char *sharp = NULL; - - value = config_params[i].print(cnt, NULL, i, thread); - - sharp = strstr(config_params[i].param_help, "#\n\n#"); - if (sharp == NULL) - sharp = strstr(config_params[i].param_help, "#"); - sharp++; - - text_help = replace(sharp, "\n#", "
"); - - if (value == NULL) - value = config_params[i].print(cnt, NULL, i, 0); - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\n" - "Thread %hu
\n
%s", - thread, thread, config_params[i].param_name, value, - TWIKI_URL, config_params[i].param_name, text_help); - - send_template(client_socket, res); - send_template_end_client(client_socket); - - free(text_help); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "%s = %s\nDone\n", config_params[i].param_name, value); - send_template_raw(client_socket, res); - } - } - } else { - /*error*/ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else { - /*error*/ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } - } else if (length_uri == 0) { - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\nThread %hu
\n" - "
\n" - "\n" - "\n" - "
\n"); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "get needs param_name\n"); - send_template_raw(client_socket, res); - } - } else { - /*error*/ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_valid_syntax, NULL); - else - response_client(client_socket, not_valid_syntax_raw, NULL); - } - } else if (!strcmp(command, "write")) { - pointer = pointer + 5; - length_uri = length_uri - 5; - if (length_uri == 0) { - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

" - "Are you sure? Yes\n", thread, thread); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - conf_print(cnt); - send_template_ini_client_raw(client_socket); - sprintf(res, "Thread %hu write\nDone\n", thread); - send_template_raw(client_socket, res); - } - } else { - /*error*/ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - - } else if (!strcmp(command, "writeyes")) { - pointer = pointer + 8; - length_uri = length_uri - 8; - if (length_uri == 0) { - conf_print(cnt); - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\nThread %hu write done !\n", - thread, thread); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "Thread %hu write\nDone\n", thread); - send_template_raw(client_socket, res); - } - } else { - /*error*/ - if (cnt[0]->conf.webcontrol_html_output) { - response_client(client_socket, not_found_response_valid_command, NULL); - } - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else { - /*error*/ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - - return 1; + char question='\0'; + char command[256] = {'\0'}; + unsigned short int i; + struct context **cnt = userdata; + + warningkill = sscanf(pointer, "%255[a-z]%c", command , &question); + if (!strcmp(command, "list")) { + pointer = pointer + 4; + length_uri = length_uri - 4; + if (length_uri == 0) { + const char *value = NULL; + char *retval = NULL; + /*call list*/ + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\nThread %hu\n
    ", thread, thread); + send_template(client_socket, res); + + for (i=0; config_params[i].param_name != NULL; i++) { + + if ((thread != 0) && (config_params[i].main_thread)) + continue; + + value = config_params[i].print(cnt, NULL, i, thread); + + if (value == NULL) { + retval = NULL; + + /* Only get the thread value for main thread */ + if (thread == 0) + config_params[i].print(cnt, &retval, i, thread); + + /* thread value*/ + + if (retval) { + + if (!strcmp(retval,"")) { + free(retval); + retval = mystrdup("No threads"); + } else { + char *temp = retval; + size_t retval_miss = 0; + size_t retval_len = strlen(retval); + unsigned short int ind = 0; + char thread_strings[1024] = {'\0'}; + + while (retval_miss != retval_len) { + while (*temp != '\n') { + thread_strings[ind++] = *temp; + retval_miss++; + temp++; + } + temp++; + thread_strings[ind++] = '<'; + thread_strings[ind++] = 'b'; + thread_strings[ind++] = 'r'; + thread_strings[ind++] = '>'; + retval_miss++; + } + free(retval); + retval = NULL; + retval = mystrdup(thread_strings); + } + + sprintf(res, "
  • %s = %s
  • \n", thread, + config_params[i].param_name, config_params[i].param_name, retval); + free(retval); + } else if (thread != 0) { + /* get the value from main thread for the rest of threads */ + value = config_params[i].print(cnt, NULL, i, 0); + + sprintf(res, "
  • %s = %s
  • \n", thread, + config_params[i].param_name, config_params[i].param_name, + value ? value : "(not defined)"); + } else { + sprintf(res, "
  • %s = %s
  • \n", thread, + config_params[i].param_name, config_params[i].param_name, + "(not defined)"); + } + + } else { + sprintf(res, "
  • %s = %s
  • \n", thread, + config_params[i].param_name, config_params[i].param_name, value); + } + send_template(client_socket, res); + } + + sprintf(res, "
<– back", thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + for (i=0; config_params[i].param_name != NULL; i++) { + value = config_params[i].print(cnt, NULL, i, thread); + if (value == NULL) + value = config_params[i].print(cnt, NULL, i, 0); + sprintf(res, "%s = %s\n", config_params[i].param_name, value); + send_template_raw(client_socket, res); + } + } + } else { + /*error*/ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (!strcmp(command, "set")) { + /* set?param_name=value */ + pointer = pointer + 3; + length_uri = length_uri - 3; + if ((length_uri != 0) && (question == '?')) { + pointer++; + length_uri--; + warningkill = sscanf(pointer,"%255[-0-9a-z_]%c", command, &question); + /*check command , question == '=' length_uri too*/ + if ((question == '=') && (command[0] != '\0')) { + length_uri = length_uri - strlen(command) - 1; + pointer = pointer + strlen(command) + 1; + /* check if command exists and type of command and not end of URI */ + i=0; + while (config_params[i].param_name != NULL) { + if ((thread != 0) && (config_params[i].main_thread)) { + i++; + continue; + } + + if (!strcasecmp(command, config_params[i].param_name)) + break; + i++; + } + + if (config_params[i].param_name) { + if (length_uri > 0) { + char Value[1024] = {'\0'}; + warningkill = sscanf(pointer,"%1023s", Value); + length_uri = length_uri - strlen(Value); + if ((length_uri == 0) && (strlen(Value) > 0)) { + /* FIXME need to assure that is a valid value */ + url_decode(Value, strlen(Value)); + conf_cmdparse(cnt + thread, config_params[i].param_name, Value); + if (cnt[0]->conf.webcontrol_html_output) { + sprintf(res, + "<– back" + "

\nThread %hu\n" + "
  • %s = %s" + "
Done", + thread, thread, thread, config_params[i].param_name, + config_params[i].param_name, Value); + + send_template_ini_client(client_socket, ini_template); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "%s = %s\nDone\n", config_params[i].param_name, Value); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + } + } else { + char *type = NULL; + type = mystrdup(config_type(&config_params[i])); + + if (!strcmp(type, "string")) { + char *value = NULL; + conf_cmdparse(cnt+thread, config_params[i].param_name, value); + free(type); + type = mystrdup("(null)"); + } else if (!strcmp(type, "int")) { + free(type); + type = mystrdup("0"); + conf_cmdparse(cnt+thread, config_params[i].param_name, type); + } else if (!strcmp(type, "short")) { + free(type); + type = mystrdup("0"); + conf_cmdparse(cnt+thread, config_params[i].param_name, type); + } else if (!strcmp(type, "bool")) { + free(type); + type = mystrdup("off"); + conf_cmdparse(cnt+thread, config_params[i].param_name, type); + } else { + free(type); + type = mystrdup("unknown"); + } + + if (cnt[0]->conf.webcontrol_html_output) { + sprintf(res, + "<– back

\n" + "Thread %hu\n
  • %s" + "= %s

Done", thread, thread, thread, + config_params[i].param_name, config_params[i].param_name, type); + + send_template_ini_client(client_socket, ini_template); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "%s = %s\nDone\n", config_params[i].param_name, type); + send_template_raw(client_socket, res); + } + free(type); + + } + } else { + /*error*/ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else { + /* Show param_name dialogue only for html output */ + if ((cnt[0]->conf.webcontrol_html_output) && (command[0] != '\0') && + (((length_uri = length_uri - strlen(command)) == 0))) { + i=0; + while (config_params[i].param_name != NULL) { + if ((thread != 0) && (config_params[i].main_thread)) { + i++; + continue; + } + + if (!strcasecmp(command, config_params[i].param_name)) + break; + i++; + } + /* param_name exists */ + if (config_params[i].param_name) { + const char *value = NULL; + char *text_help = NULL; + char *sharp = NULL; + + value = config_params[i].print(cnt, NULL, i, thread); + + sharp = strstr(config_params[i].param_help, "#\n\n#"); + if (sharp == NULL) + sharp = strstr(config_params[i].param_help, "#"); + sharp++; + + text_help = replace(sharp, "\n#", "
"); + + send_template_ini_client(client_socket, ini_template); + if (!strcmp ("bool", config_type(&config_params[i]))) { + char option[80] = {'\0'}; + + if ((value == NULL) && (thread != 0)) + value = config_params[i].print(cnt, NULL, i, 0); + + if (!strcmp ("on", value)) + sprintf(option, "\n" + "\n"); + else + sprintf(option, "\n" + "\n"); + + sprintf(res, "<– back

\n" + "Thread %hu\n" + "
\n" + "%s \n" + "    " + "[help]" + "
\n
%s", thread, thread, + config_params[i].param_name, config_params[i].param_name, + option, TWIKI_URL, config_params[i].param_name, text_help); + } else { + + if (value == NULL) { + if (thread != 0) + /* get the value from main thread for the rest of threads */ + value = config_params[i].print(cnt, NULL, i, 0); + if (value == NULL) value = ""; + } + sprintf(res, "<– back

\n" + "Thread %hu\n
\n" + "%s \n" + "\n" + "    " + "[help]" + "
\n
%s", thread, thread, + config_params[i].param_name, config_params[i].param_name, + value, TWIKI_URL, config_params[i].param_name, text_help); + } + + send_template(client_socket, res); + send_template_end_client(client_socket); + free(text_help); + } else { + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else { + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } + } else if (length_uri == 0) { + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, set_template); + sprintf(res, "<– back

\nThread %hu\n" + "
\n\n
\n" + "
\n" + "\n" + "\n" + "
\n"); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "set needs param_name=value\n"); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (!strcmp(command, "get")) { + /* get?query=param_name */ + pointer = pointer + 3; + length_uri = length_uri - 3; + + if ((length_uri > 7) && (question == '?')) { + /* 8 -> query=param_name FIXME minimum length param_name */ + pointer++; + length_uri--; + warningkill = sscanf(pointer,"%255[-0-9a-z]%c", command, &question); + + if ((question == '=') && (!strcmp(command, "query"))) { + pointer = pointer + 6; + length_uri = length_uri - 6; + warningkill = sscanf(pointer, "%255[-0-9a-z_]", command); + /*check if command exist, length_uri too*/ + length_uri = length_uri-strlen(command); + + if (length_uri == 0) { + const char *value = NULL; + i = 0; + while (config_params[i].param_name != NULL) { + if ((thread != 0) && (config_params[i].main_thread)) { + i++; + continue; + } + + if (!strcasecmp(command, config_params[i].param_name)) + break; + i++; + } + /* FIXME bool values or commented values maybe that should be + solved with config_type */ + + if (config_params[i].param_name) { + const char *type = NULL; + type = config_type(&config_params[i]); + if (!strcmp(type, "unknown")) { + /*error doesn't exists this param_name */ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + return 1; + } else { + char *text_help = NULL; + char *sharp = NULL; + + value = config_params[i].print(cnt, NULL, i, thread); + + sharp = strstr(config_params[i].param_help, "#\n\n#"); + if (sharp == NULL) + sharp = strstr(config_params[i].param_help, "#"); + sharp++; + + text_help = replace(sharp, "\n#", "
"); + + if (value == NULL) + value = config_params[i].print(cnt, NULL, i, 0); + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\n" + "Thread %hu
\n
%s", + thread, thread, config_params[i].param_name, value, + TWIKI_URL, config_params[i].param_name, text_help); + + send_template(client_socket, res); + send_template_end_client(client_socket); + + free(text_help); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "%s = %s\nDone\n", config_params[i].param_name, value); + send_template_raw(client_socket, res); + } + } + } else { + /*error*/ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else { + /*error*/ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } + } else if (length_uri == 0) { + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\nThread %hu
\n" + "
\n" + "\n" + "\n" + "
\n"); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "get needs param_name\n"); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + } + } else if (!strcmp(command, "write")) { + pointer = pointer + 5; + length_uri = length_uri - 5; + if (length_uri == 0) { + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

" + "Are you sure? Yes\n", thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + conf_print(cnt); + send_template_ini_client_raw(client_socket); + sprintf(res, "Thread %hu write\nDone\n", thread); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + + } else if (!strcmp(command, "writeyes")) { + pointer = pointer + 8; + length_uri = length_uri - 8; + if (length_uri == 0) { + conf_print(cnt); + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\nThread %hu write done !\n", + thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "Thread %hu write\nDone\n", thread); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.webcontrol_html_output) { + response_client(client_socket, not_found_response_valid_command, NULL); + } + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else { + /*error*/ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + + return 1; } @@ -896,175 +899,175 @@ static unsigned short int config(char *pointer, char *res, unsigned short int le */ static unsigned short int action(char *pointer, char *res, unsigned short int length_uri, - unsigned short int thread, int client_socket, void *userdata) + unsigned short int thread, int client_socket, void *userdata) { - /* parse action commands */ - char command[256] = {'\0'}; - struct context **cnt = userdata; - unsigned short int i = 0; - - warningkill = sscanf(pointer, "%255[a-z]" , command); - if (!strcmp(command, "makemovie")) { - pointer = pointer + 9; - length_uri = length_uri - 9; - if (length_uri == 0) { - /*call makemovie*/ - - if (thread == 0) { - while (cnt[++i]) - cnt[i]->makemovie = 1; - } else { - cnt[thread]->makemovie = 1; - } - - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\n" - "makemovie for thread %hu done
\n", thread, thread); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "makemovie for thread %hu\nDone\n", thread); - send_template_raw(client_socket, res); - } - } else { - /*error*/ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else if (!strcmp(command, "snapshot")) { - pointer = pointer + 8; - length_uri = length_uri - 8; - if (length_uri == 0) { - /*call snapshot*/ - - if (thread == 0) { - while (cnt[++i]) - cnt[i]->snapshot = 1; - } else { - cnt[thread]->snapshot = 1; - } - - cnt[thread]->snapshot = 1; - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\n" - "snapshot for thread %hu done
\n", thread, thread); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "snapshot for thread %hu\nDone\n", thread); - send_template_raw(client_socket, res); - } - } else { - /*error*/ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else if (!strcmp(command, "restart")) { - pointer = pointer + 7; - length_uri = length_uri - 7; - if (length_uri == 0) { - /*call restart*/ - - if (thread == 0) { - motion_log(LOG_DEBUG, 0, "%s: httpd restart", __FUNCTION__); - kill(getpid(),SIGHUP); - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "restart in progress ... bye
\nHome"); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "restart in progress ...\nDone\n"); - send_template_raw(client_socket, res); - } - return 0; // to restart - } else { - motion_log(LOG_DEBUG, 0, "%s: httpd restart thread %d", __FUNCTION__, thread); - if (cnt[thread]->running) { - cnt[thread]->makemovie = 1; - cnt[thread]->finish = 1; - } - cnt[thread]->restart = 1; - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\n" - "restart for thread %hu done
\n", thread, thread); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "restart for thread %hu\nDone\n", thread); - send_template_raw(client_socket, res); - } - } - } else { - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else if (!strcmp(command, "quit")) { - pointer = pointer + 4; - length_uri = length_uri - 4; - if (length_uri == 0) { - /*call quit*/ - - if (thread == 0) { - motion_log(LOG_DEBUG, 0, "%s: httpd quit", __FUNCTION__); - kill(getpid(),SIGQUIT); - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "quit in progress ... bye"); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res,"quit in progress ... bye\nDone\n"); - send_template_raw(client_socket, res); - } - return 0; // to quit - } else { - motion_log(LOG_DEBUG, 0, "%s: httpd quit thread %d", __FUNCTION__, thread); - cnt[thread]->restart = 0; - cnt[thread]->makemovie = 1; - cnt[thread]->finish = 1; - cnt[thread]->watchdog = WATCHDOG_OFF; - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\n" - "quit for thread %hu done
\n", thread, thread); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "quit for thread %hu\nDone\n", thread); - send_template_raw(client_socket, res); - } - } - } else { - /*error*/ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else { - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - - return 1; + /* parse action commands */ + char command[256] = {'\0'}; + struct context **cnt = userdata; + unsigned short int i = 0; + + warningkill = sscanf(pointer, "%255[a-z]" , command); + if (!strcmp(command, "makemovie")) { + pointer = pointer + 9; + length_uri = length_uri - 9; + if (length_uri == 0) { + /*call makemovie*/ + + if (thread == 0) { + while (cnt[++i]) + cnt[i]->makemovie = 1; + } else { + cnt[thread]->makemovie = 1; + } + + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\n" + "makemovie for thread %hu done
\n", thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "makemovie for thread %hu\nDone\n", thread); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (!strcmp(command, "snapshot")) { + pointer = pointer + 8; + length_uri = length_uri - 8; + if (length_uri == 0) { + /*call snapshot*/ + + if (thread == 0) { + while (cnt[++i]) + cnt[i]->snapshot = 1; + } else { + cnt[thread]->snapshot = 1; + } + + cnt[thread]->snapshot = 1; + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\n" + "snapshot for thread %hu done
\n", thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "snapshot for thread %hu\nDone\n", thread); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (!strcmp(command, "restart")) { + pointer = pointer + 7; + length_uri = length_uri - 7; + if (length_uri == 0) { + /*call restart*/ + + if (thread == 0) { + motion_log(LOG_DEBUG, 0, "%s: httpd restart", __FUNCTION__); + kill(getpid(),SIGHUP); + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "restart in progress ... bye
\nHome"); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "restart in progress ...\nDone\n"); + send_template_raw(client_socket, res); + } + return 0; // to restart + } else { + motion_log(LOG_DEBUG, 0, "%s: httpd restart thread %d", __FUNCTION__, thread); + if (cnt[thread]->running) { + cnt[thread]->makemovie = 1; + cnt[thread]->finish = 1; + } + cnt[thread]->restart = 1; + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\n" + "restart for thread %hu done
\n", thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "restart for thread %hu\nDone\n", thread); + send_template_raw(client_socket, res); + } + } + } else { + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (!strcmp(command, "quit")) { + pointer = pointer + 4; + length_uri = length_uri - 4; + if (length_uri == 0) { + /*call quit*/ + + if (thread == 0) { + motion_log(LOG_DEBUG, 0, "%s: httpd quit", __FUNCTION__); + kill(getpid(),SIGQUIT); + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "quit in progress ... bye"); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res,"quit in progress ... bye\nDone\n"); + send_template_raw(client_socket, res); + } + return 0; // to quit + } else { + motion_log(LOG_DEBUG, 0, "%s: httpd quit thread %d", __FUNCTION__, thread); + cnt[thread]->restart = 0; + cnt[thread]->makemovie = 1; + cnt[thread]->finish = 1; + cnt[thread]->watchdog = WATCHDOG_OFF; + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\n" + "quit for thread %hu done
\n", thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "quit for thread %hu\nDone\n", thread); + send_template_raw(client_socket, res); + } + } + } else { + /*error*/ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else { + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + + return 1; } /* @@ -1073,160 +1076,160 @@ static unsigned short int action(char *pointer, char *res, unsigned short int le return 1 to exit from function. */ -static unsigned short int detection(char *pointer, char *res, unsigned short int length_uri, unsigned short int thread, - int client_socket, void *userdata) +static unsigned short int detection(char *pointer, char *res, unsigned short int length_uri, + unsigned short int thread, int client_socket, void *userdata) { - char command[256] = {'\0'}; - struct context **cnt = userdata; - unsigned short int i = 0; - - warningkill = sscanf(pointer, "%255[a-z]" , command); - if (!strcmp(command, "status")) { - pointer = pointer + 6; - length_uri = length_uri - 6; - if (length_uri == 0) { - /*call status*/ - - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

Thread %hu" - " Detection status %s\n", thread, thread, - (!cnt[thread]->running)? "NOT RUNNING": (cnt[thread]->pause)? "PAUSE":"ACTIVE"); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - sprintf(res, "Thread %hu Detection status %s\n", thread, - (!cnt[thread]->running)? "NOT RUNNING": (cnt[thread]->pause)? "PAUSE":"ACTIVE"); - send_template_ini_client_raw(client_socket); - send_template_raw(client_socket, res); - } - } else { - /*error*/ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else if (!strcmp(command, "start")) { - pointer = pointer + 5; - length_uri = length_uri - 5; - if (length_uri == 0) { - /*call start*/ - - if (thread == 0) { - do { - cnt[i]->pause = 0; - } while (cnt[++i]); - } else { - cnt[thread]->pause = 0; - } - - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\nThread %hu" - " Detection resumed\n", thread, thread); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "Thread %hu Detection resumed\nDone\n", thread); - send_template_raw(client_socket, res); - } - } else { - /*error*/ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else if (!strcmp(command, "pause")) { - pointer = pointer + 5; - length_uri = length_uri - 5; - if (length_uri == 0) { - /*call pause*/ - - if (thread == 0) { - do { - cnt[i]->pause = 1; - } while (cnt[++i]); - } else { - cnt[thread]->pause = 1; - } - - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\nThread %hu" - " Detection paused\n", thread, thread); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "Thread %hu Detection paused\nDone\n", thread); - send_template_raw(client_socket, res); - } - } else { - /*error*/ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else if (!strcmp(command, "connection")) { - pointer = pointer + 10; - length_uri = length_uri - 10; - if (length_uri == 0) { - - /*call connection*/ - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\n", thread); - send_template(client_socket, res); - if (thread == 0) { - do{ - sprintf(res, "Thread %hu %s
\n", i, - (!cnt[i]->running)? "NOT RUNNING" : - (cnt[i]->lost_connection)?CONNECTION_KO:CONNECTION_OK); - send_template(client_socket, res); - }while (cnt[++i]); - }else{ - sprintf(res, "Thread %hu %s\n", thread, - (!cnt[thread]->running)? "NOT RUNNING" : - (cnt[thread]->lost_connection)? CONNECTION_KO: CONNECTION_OK); - send_template(client_socket, res); - } - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - if (thread == 0) { - do{ - sprintf(res, "Thread %hu %s\n", i, - (!cnt[i]->running)? "NOT RUNNING" : + char command[256] = {'\0'}; + struct context **cnt = userdata; + unsigned short int i = 0; + + warningkill = sscanf(pointer, "%255[a-z]" , command); + if (!strcmp(command, "status")) { + pointer = pointer + 6; + length_uri = length_uri - 6; + if (length_uri == 0) { + /*call status*/ + + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

Thread %hu" + " Detection status %s\n", thread, thread, + (!cnt[thread]->running)? "NOT RUNNING": (cnt[thread]->pause)? "PAUSE":"ACTIVE"); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + sprintf(res, "Thread %hu Detection status %s\n", thread, + (!cnt[thread]->running)? "NOT RUNNING": (cnt[thread]->pause)? "PAUSE":"ACTIVE"); + send_template_ini_client_raw(client_socket); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (!strcmp(command, "start")) { + pointer = pointer + 5; + length_uri = length_uri - 5; + if (length_uri == 0) { + /*call start*/ + + if (thread == 0) { + do { + cnt[i]->pause = 0; + } while (cnt[++i]); + } else { + cnt[thread]->pause = 0; + } + + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\nThread %hu" + " Detection resumed\n", thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "Thread %hu Detection resumed\nDone\n", thread); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (!strcmp(command, "pause")) { + pointer = pointer + 5; + length_uri = length_uri - 5; + if (length_uri == 0) { + /*call pause*/ + + if (thread == 0) { + do { + cnt[i]->pause = 1; + } while (cnt[++i]); + } else { + cnt[thread]->pause = 1; + } + + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\nThread %hu" + " Detection paused\n", thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "Thread %hu Detection paused\nDone\n", thread); + send_template_raw(client_socket, res); + } + } else { + /*error*/ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (!strcmp(command, "connection")) { + pointer = pointer + 10; + length_uri = length_uri - 10; + + if (length_uri == 0) { + /*call connection*/ + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\n", thread); + send_template(client_socket, res); + if (thread == 0) { + do{ + sprintf(res, "Thread %hu %s
\n", i, + (!cnt[i]->running)? "NOT RUNNING" : + (cnt[i]->lost_connection)?CONNECTION_KO:CONNECTION_OK); + send_template(client_socket, res); + } while (cnt[++i]); + } else { + sprintf(res, "Thread %hu %s\n", thread, + (!cnt[thread]->running)? "NOT RUNNING" : + (cnt[thread]->lost_connection)? CONNECTION_KO: CONNECTION_OK); + send_template(client_socket, res); + } + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + if (thread == 0) { + do { + sprintf(res, "Thread %hu %s\n", i, + (!cnt[i]->running)? "NOT RUNNING" : (cnt[i]->lost_connection)? CONNECTION_KO: CONNECTION_OK); - send_template_raw(client_socket, res); - }while (cnt[++i]); - }else{ - sprintf(res, "Thread %hu %s\n", thread, - (!cnt[thread]->running)? "NOT RUNNING" : - (cnt[thread]->lost_connection)? CONNECTION_KO: CONNECTION_OK); - send_template_raw(client_socket, res); - } - - } - }else{ - /*error*/ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else { - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - - return 1; + send_template_raw(client_socket, res); + } while (cnt[++i]); + } else { + sprintf(res, "Thread %hu %s\n", thread, + (!cnt[thread]->running)? "NOT RUNNING" : + (cnt[thread]->lost_connection)? CONNECTION_KO: CONNECTION_OK); + send_template_raw(client_socket, res); + } + + } + } else { + /*error*/ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else { + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + + return 1; } @@ -1236,927 +1239,925 @@ static unsigned short int detection(char *pointer, char *res, unsigned short int return 1 to exit from function. */ -static unsigned short int track(char *pointer, char *res, unsigned short int length_uri, unsigned short int thread, - int client_socket, void *userdata) +static unsigned short int track(char *pointer, char *res, unsigned short int length_uri, + unsigned short int thread, int client_socket, void *userdata) { - char question='\0'; - char command[256] = {'\0'}; - struct context **cnt = userdata; - - warningkill = sscanf(pointer, "%255[a-z]%c", command, &question); - if (!strcmp(command, "set")) { - pointer = pointer+3; - length_uri = length_uri-3; - /* FIXME need to check each value */ - /* Relative movement set?pan=0&tilt=0 | set?pan=0 | set?tilt=0*/ - /* Absolute movement set?x=0&y=0 | set?x=0 | set?y=0 */ - - if ((question == '?') && (length_uri > 2)) { - char panvalue[12] = {'\0'}, tiltvalue[12] = {'\0'}; - char x_value[12] = {'\0'}, y_value[12] = {'\0'}; - struct context *setcnt; - int pan = 0, tilt = 0, X = 0 , Y = 0; - - pointer++; - length_uri--; - /* set?pan=value&tilt=value */ - /* set?x=value&y=value */ - /* pan= or x= | tilt= or y= */ - - warningkill = sscanf(pointer, "%255[a-z]%c" , command, &question); - - if (( question != '=' ) || (command[0] == '\0')) { - /* no valid syntax */ - motion_log(LOG_WARNING, 0, "%s: httpd debug race 1", __FUNCTION__); - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_valid_syntax, NULL); - else - response_client(client_socket, not_valid_syntax_raw, NULL); - return 1; - } - - pointer++; - length_uri--; - - /* Check first parameter */ - - if (!strcmp(command, "pan")) { - pointer = pointer + 3; - length_uri = length_uri - 3; - pan = 1; - if ((warningkill = sscanf(pointer, "%10[-0-9]", panvalue))) { - pointer = pointer + strlen(panvalue); - length_uri = length_uri - strlen(panvalue); - } - } - else if (!strcmp(command, "tilt")) { - pointer = pointer + 4; - length_uri = length_uri - 4; - tilt = 1; - if ((warningkill = sscanf(pointer, "%10[-0-9]", tiltvalue))) { - pointer = pointer + strlen(tiltvalue); - length_uri = length_uri - strlen(tiltvalue); - } - } - else if (!strcmp(command, "x")) { - pointer++; - length_uri--; - X = 1; - if ((warningkill = sscanf(pointer, "%10[-0-9]", x_value))) { - pointer = pointer + strlen(x_value); - length_uri = length_uri - strlen(x_value); - } - } - else if (!strcmp(command, "y")) { - pointer++; - length_uri--; - Y = 1; - if ((warningkill = sscanf(pointer, "%10[-0-9]" , y_value))) { - pointer = pointer + strlen(y_value); - length_uri = length_uri - strlen(y_value); - } - } else { - /* no valid syntax */ - motion_log(LOG_WARNING, 0, "%s: httpd debug race 2", __FUNCTION__); - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_valid_syntax, NULL); - else - response_client(client_socket, not_valid_syntax_raw, NULL); - return 1; - } - - /* first value check for error */ - - if ( !warningkill ) { - motion_log(LOG_WARNING, 0, "%s: httpd debug race 3", __FUNCTION__); - /* error value */ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, error_value, NULL); - else - response_client(client_socket, error_value_raw, NULL); - return 1; - } - - /* Only one parameter (pan= ,tilt= ,x= ,y= ) */ - if (length_uri == 0) { - if (pan) { - struct coord cent; - struct context *pancnt; - - /* move pan */ - - pancnt = cnt[thread]; - cent.width = pancnt->imgs.width; - cent.height = pancnt->imgs.height; - cent.y = 0; - cent.x = atoi(panvalue); - // Add the number of frame to skip for motion detection - cnt[thread]->moved = track_move(pancnt, pancnt->video_dev, ¢, &pancnt->imgs, 1); - if (cnt[thread]->moved) { - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

" - "Thread %hu
\n" - "track set relative pan=%s
\n", - thread, thread, panvalue); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "track set relative pan=%s\nDone\n", panvalue); - send_template_raw(client_socket, res); - } - } else { - /*error in track action*/ - if (cnt[0]->conf.webcontrol_html_output) { - sprintf(res, "<– back

" - "Thread %hu\n", thread, thread); - response_client(client_socket, track_error, res); - } - else - response_client(client_socket, track_error_raw, NULL); - } - } else if (tilt) { - struct coord cent; - struct context *tiltcnt; - - /* move tilt */ - - tiltcnt = cnt[thread]; - cent.width = tiltcnt->imgs.width; - cent.height = tiltcnt->imgs.height; - cent.x = 0; - cent.y = atoi(tiltvalue); - // Add the number of frame to skip for motion detection - cnt[thread]->moved = track_move(tiltcnt, tiltcnt->video_dev, ¢, &tiltcnt->imgs, 1); - if (cnt[thread]->moved) { - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

" - "Thread %hu
\n" - "track set relative tilt=%s\n", - thread, thread, tiltvalue); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "track set relative tilt=%s\nDone\n", tiltvalue); - send_template_raw(client_socket, res); - } - } else { - /*error in track action*/ - if (cnt[0]->conf.webcontrol_html_output) { - sprintf(res, "<– back

" - "Thread %hu\n", thread, thread); - response_client(client_socket, track_error, res); - } - else - response_client(client_socket, track_error_raw, NULL); - } - } else if (X) { - /* X */ - setcnt = cnt[thread]; - // 1000 is out of range for pwc - cnt[thread]->moved = track_center(setcnt, setcnt->video_dev, 1, atoi(x_value), 1000); - if (cnt[thread]->moved) { - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

" - "Thread %hu
\n" - "track set absolute x=%s\n", - thread, thread, x_value); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "track set absolute x=%s\nDone\n", x_value); - send_template_raw(client_socket, res); - } - } else { - /*error in track action*/ - if (cnt[0]->conf.webcontrol_html_output) { - sprintf(res, "<– back

" - "Thread %hu\n", thread, thread); - response_client(client_socket, track_error, res); - } - else - response_client(client_socket, track_error_raw, NULL); - } - - } else { - /* Y */ - setcnt = cnt[thread]; - // 1000 is out of range for pwc - cnt[thread]->moved = track_center(setcnt, setcnt->video_dev, 1, 1000, atoi(y_value)); - if (cnt[thread]->moved) { - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

" - "Thread %hu
\n" - "track set absolute y=%s
\n", - thread, thread, y_value); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "track set absolute y=%s\nDone\n", y_value); - send_template_raw(client_socket, res); - } - } else { - /*error in track action*/ - if (cnt[0]->conf.webcontrol_html_output) { - sprintf(res, "<– back

" - "Thread %hu\n", thread, thread); - response_client(client_socket, track_error, res); - } - else - response_client(client_socket, track_error_raw, NULL); - } - } - return 1; - } - - - /* Check Second parameter */ - - warningkill = sscanf(pointer, "%c%255[a-z]" , &question, command); - if ( ( question != '&' ) || (command[0] == '\0') ) { - motion_log(LOG_WARNING, 0, "%s: httpd debug race 4", __FUNCTION__); - if ( strstr(pointer, "&")) { - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, error_value, NULL); - else - response_client(client_socket, error_value_raw, NULL); - } - /* no valid syntax */ - else{ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_valid_syntax, NULL); - else - response_client(client_socket, not_valid_syntax_raw, NULL); - } - return 1; - } - - pointer++; - length_uri--; - - if (!strcmp(command, "pan")) { - pointer = pointer + 3; - length_uri = length_uri - 3; - if ( (pan) || (!tilt) || (X) || (Y) ) { - motion_log(LOG_WARNING, 0, "%s: httpd debug race 5", __FUNCTION__); - /* no valid syntax */ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_valid_syntax, NULL); - else - response_client(client_socket, not_valid_syntax_raw, NULL); - return 1; - } - pan=2; - warningkill = sscanf(pointer, "%c%10[-0-9]" , &question, panvalue); - } - else if (!strcmp(command, "tilt")) { - pointer = pointer + 4; - length_uri = length_uri - 4; - if ( (tilt) || (!pan) || (X) || (Y) ) { - /* no valid syntax */ - motion_log(LOG_WARNING, 0, "%s: httpd debug race 6", __FUNCTION__); - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_valid_syntax, NULL); - else - response_client(client_socket, not_valid_syntax_raw, NULL); - return 1; - } - tilt = 2; - warningkill = sscanf(pointer, "%c%10[-0-9]" , &question, tiltvalue); - } - else if (!strcmp(command, "x")) { - pointer++; - length_uri--; - if ( (X) || (!Y) || (pan) || (tilt) ) { - motion_log(LOG_WARNING, 0, "%s: httpd debug race 7", __FUNCTION__); - - /* no valid syntax */ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_valid_syntax, NULL); - else - response_client(client_socket, not_valid_syntax_raw, NULL); - return 1; - } - X = 2; - warningkill = sscanf(pointer, "%c%10[-0-9]" , &question, x_value); - } - else if (!strcmp(command, "y")) { - pointer++; - length_uri--; - if ( (Y) || (!X) || (pan) || (tilt) ) { - motion_log(LOG_WARNING, 0, "%s: httpd debug race 8", __FUNCTION__); - /* no valid syntax */ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_valid_syntax, NULL); - else - response_client(client_socket, not_valid_syntax_raw, NULL); - return 1; - } - Y = 2; - warningkill = sscanf(pointer, "%c%10[-0-9]" , &question, y_value); - } else { - motion_log(LOG_WARNING, 0, "%s: httpd debug race 9", __FUNCTION__); - /* no valid syntax */ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_valid_syntax, NULL); - else - response_client(client_socket, not_valid_syntax_raw, NULL); - return 1; - } - - /* Second value check */ - - if ( ( warningkill < 2 ) && (question != '=') ) { - motion_log(LOG_WARNING, 0, "%s: httpd debug race 10", __FUNCTION__); - /* no valid syntax */ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_valid_syntax, NULL); - else - response_client(client_socket, not_valid_syntax_raw, NULL); - return 1; - }else if (( question == '=') && ( warningkill == 1)) { - motion_log(LOG_WARNING, 0, "%s: httpd debug race 11", __FUNCTION__); - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, error_value, NULL); - else - response_client(client_socket, error_value_raw, NULL); - return 1; - } - - - if (pan == 2) { - pointer = pointer + strlen(panvalue) + 1; - length_uri = length_uri - strlen(panvalue) - 1; - } - else if (tilt == 2) { - pointer = pointer + strlen(tiltvalue) + 1; - length_uri = length_uri - strlen(tiltvalue) - 1; - } - else if (X == 2) { - pointer = pointer + strlen(x_value) + 1; - length_uri = length_uri - strlen(x_value) - 1; - } - else{ - pointer = pointer + strlen(y_value) + 1; - length_uri = length_uri - strlen(y_value) - 1; - } - - - - if (length_uri != 0) { - motion_log(LOG_WARNING, 0, "%s: httpd debug race 12", __FUNCTION__); - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, error_value, NULL); - else - response_client(client_socket, error_value_raw, NULL); - return 1; - } - - - /* track set absolute ( x , y )*/ - - if ( X && Y ) { - setcnt = cnt[thread]; - cnt[thread]->moved = track_center(setcnt, setcnt->video_dev, 1, atoi(x_value), atoi(y_value)); - if (cnt[thread]->moved) { - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

" - "Thread %hu
\n" - "track absolute set x=%s y=%s
\n", - thread, thread, x_value, y_value); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "track absolute set x=%s y=%s\nDone\n", x_value, y_value); - send_template_raw(client_socket, res); - } - } else { - /*error in track action*/ - if (cnt[0]->conf.webcontrol_html_output) { - sprintf(res, "<– back

" - "Thread %hu\n", thread, thread); - response_client(client_socket, track_error, res); - } - else - response_client(client_socket, track_error_raw, NULL); - } - /* track set relative ( pan , tilt )*/ - } else { - struct coord cent; - struct context *relativecnt; - - /* move pan */ - - relativecnt = cnt[thread]; - cent.width = relativecnt->imgs.width; - cent.height = relativecnt->imgs.height; - cent.y = 0; - cent.x = atoi(panvalue); - // Add the number of frame to skip for motion detection - cnt[thread]->moved = track_move(relativecnt, relativecnt->video_dev, ¢, &relativecnt->imgs, 1); - if (cnt[thread]->moved) { - /* move tilt */ - relativecnt = cnt[thread]; - cent.width = relativecnt->imgs.width; - cent.height = relativecnt->imgs.height; - cent.x = 0; - cent.y = atoi(tiltvalue); - // SLEEP(1,0); - cnt[thread]->moved = track_move(relativecnt, relativecnt->video_dev, ¢, &relativecnt->imgs, 1); - if (cnt[thread]->moved) { - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

" - "Thread %hu
\n" - "track relative pan=%s tilt=%s\n", - thread, thread, panvalue, tiltvalue); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "track relative pan=%s tilt=%s\nDone\n", panvalue, tiltvalue); - send_template_raw(client_socket, res); - } - return 1; - } - else{ - /*error in track tilt*/ - if (cnt[0]->conf.webcontrol_html_output) { - sprintf(res, "<– back

" - "Thread %hu\n", thread, thread); - response_client(client_socket, track_error, res); - }else response_client(client_socket, track_error_raw, NULL); - } - } - - /*error in track pan*/ - if (cnt[0]->conf.webcontrol_html_output) { - sprintf(res, "<– back

Thread %hu\n", - thread, thread); - response_client(client_socket, track_error, res); - } else - response_client(client_socket, track_error_raw, NULL); - } - } else if (length_uri == 0) { - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\nThread %hu
\n" - "
\n" - "Pan\n" - "Tilt\n" - "\n" - "
\n" - "
\n" - "X\n" - "Y\n" - "\n" - "
\n", thread, thread); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "set needs a pan/tilt or x/y values\n"); - send_template_raw(client_socket, res); - } - } else { - /* error not valid command */ - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else if (!strcmp(command, "center")) { - pointer = pointer+6; - length_uri = length_uri-6; - if (length_uri==0) { - struct context *setcnt; - setcnt = cnt[thread]; - // 1000 is out of range for pwc - cnt[thread]->moved = track_center(setcnt, setcnt->video_dev, 1, 0, 0); - - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\nThread %hu" - "
track set center", thread, thread); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - sprintf(res, "Thread %hu\n track set center\nDone\n", thread); - send_template_ini_client_raw(client_socket); - send_template_raw(client_socket, res); - } - } else { - /* error not valid command */ - if (cnt[0]->conf.webcontrol_html_output) { - response_client(client_socket, not_found_response_valid_command, NULL); - } else response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else if (!strcmp(command, "status")) { - pointer = pointer+6; - length_uri = length_uri-6; - if (length_uri==0) { - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\nThread %hu" - "
track auto %s", thread, thread, - (cnt[thread]->track.active)? "enabled":"disabled"); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - sprintf(res, "Thread %hu\n track auto %s\nDone\n", thread, - (cnt[thread]->track.active)? "enabled":"disabled"); - send_template_ini_client_raw(client_socket); - send_template_raw(client_socket, res); - } - } else { - /* error not valid command */ - if (cnt[0]->conf.webcontrol_html_output) { - response_client(client_socket, not_found_response_valid_command, NULL); - } else response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else if (!strcmp(command, "auto")) { - pointer = pointer + 4; - length_uri = length_uri - 4; - if ((question == '?') && (length_uri > 0)) { - char query[256] = {'\0'}; - pointer++; - length_uri--; - /* value= */ - - warningkill = sscanf(pointer, "%255[a-z]%c", query, &question); - if ((question == '=') && (!strcmp(query, "value")) ) { - pointer = pointer + 6; - length_uri = length_uri - 6; - warningkill = sscanf(pointer, "%255[-0-9a-z]" , command); - if ((command != NULL) && (strlen(command) > 0)) { - struct context *autocnt; - - /* auto value=0|1|status*/ - - if (!strcmp(command, "status")) { - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

" - "Thread %hu
" - "track auto %s", thread, thread, - (cnt[thread]->track.active)? "enabled":"disabled"); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - sprintf(res, "Thread %hu\n track auto %s\nDone\n", thread, - (cnt[thread]->track.active)? "enabled":"disabled"); - send_template_ini_client_raw(client_socket); - send_template_raw(client_socket, res); - } - } else { - int active; - active = atoi(command); - /* CHECK */ - if (active > -1 && active < 2) { - autocnt = cnt[thread]; - autocnt->track.active = active; - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

" - "Thread %hu" - "
track auto %s
", thread, thread, - active ? "enabled":"disabled"); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "track auto %s\nDone\n", active ? "enabled":"disabled"); - send_template_raw(client_socket, res); - } - } else { - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } - } else { - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else { - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } - else if (length_uri == 0) { - - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\nThread %hu\n" - "
\n" - "
\n", thread, thread, (cnt[thread]->track.active) ? "selected":"", - (cnt[thread]->track.active) ? "selected":""); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "auto accepts only 0,1 or status as valid value\n"); - send_template_raw(client_socket, res); - } - } else { - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else { - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - - return 1; + char question='\0'; + char command[256] = {'\0'}; + struct context **cnt = userdata; + + warningkill = sscanf(pointer, "%255[a-z]%c", command, &question); + if (!strcmp(command, "set")) { + pointer = pointer+3; + length_uri = length_uri-3; + /* FIXME need to check each value */ + /* Relative movement set?pan=0&tilt=0 | set?pan=0 | set?tilt=0*/ + /* Absolute movement set?x=0&y=0 | set?x=0 | set?y=0 */ + + if ((question == '?') && (length_uri > 2)) { + char panvalue[12] = {'\0'}, tiltvalue[12] = {'\0'}; + char x_value[12] = {'\0'}, y_value[12] = {'\0'}; + struct context *setcnt; + int pan = 0, tilt = 0, X = 0 , Y = 0; + + pointer++; + length_uri--; + /* set?pan=value&tilt=value */ + /* set?x=value&y=value */ + /* pan= or x= | tilt= or y= */ + + warningkill = sscanf(pointer, "%255[a-z]%c" , command, &question); + + if ((question != '=') || (command[0] == '\0')) { + /* no valid syntax */ + motion_log(LOG_WARNING, 0, "%s: httpd debug race 1", __FUNCTION__); + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + return 1; + } + + pointer++; + length_uri--; + + /* Check first parameter */ + + if (!strcmp(command, "pan")) { + pointer = pointer + 3; + length_uri = length_uri - 3; + pan = 1; + if ((warningkill = sscanf(pointer, "%10[-0-9]", panvalue))) { + pointer = pointer + strlen(panvalue); + length_uri = length_uri - strlen(panvalue); + } + } + else if (!strcmp(command, "tilt")) { + pointer = pointer + 4; + length_uri = length_uri - 4; + tilt = 1; + if ((warningkill = sscanf(pointer, "%10[-0-9]", tiltvalue))) { + pointer = pointer + strlen(tiltvalue); + length_uri = length_uri - strlen(tiltvalue); + } + } + else if (!strcmp(command, "x")) { + pointer++; + length_uri--; + X = 1; + if ((warningkill = sscanf(pointer, "%10[-0-9]", x_value))) { + pointer = pointer + strlen(x_value); + length_uri = length_uri - strlen(x_value); + } + } + else if (!strcmp(command, "y")) { + pointer++; + length_uri--; + Y = 1; + if ((warningkill = sscanf(pointer, "%10[-0-9]" , y_value))) { + pointer = pointer + strlen(y_value); + length_uri = length_uri - strlen(y_value); + } + } else { + /* no valid syntax */ + motion_log(LOG_WARNING, 0, "%s: httpd debug race 2", __FUNCTION__); + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + return 1; + } + + /* first value check for error */ + + if (!warningkill) { + motion_log(LOG_WARNING, 0, "%s: httpd debug race 3", __FUNCTION__); + /* error value */ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, error_value, NULL); + else + response_client(client_socket, error_value_raw, NULL); + return 1; + } + + /* Only one parameter (pan= ,tilt= ,x= ,y= ) */ + if (length_uri == 0) { + if (pan) { + struct coord cent; + struct context *pancnt; + + /* move pan */ + + pancnt = cnt[thread]; + cent.width = pancnt->imgs.width; + cent.height = pancnt->imgs.height; + cent.y = 0; + cent.x = atoi(panvalue); + // Add the number of frame to skip for motion detection + cnt[thread]->moved = track_move(pancnt, pancnt->video_dev, ¢, &pancnt->imgs, 1); + if (cnt[thread]->moved) { + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

" + "Thread %hu
\n" + "track set relative pan=%s
\n", + thread, thread, panvalue); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "track set relative pan=%s\nDone\n", panvalue); + send_template_raw(client_socket, res); + } + } else { + /*error in track action*/ + if (cnt[0]->conf.webcontrol_html_output) { + sprintf(res, "<– back

" + "Thread %hu\n", thread, thread); + response_client(client_socket, track_error, res); + } else { + response_client(client_socket, track_error_raw, NULL); + } + } + } else if (tilt) { + struct coord cent; + struct context *tiltcnt; + + /* move tilt */ + + tiltcnt = cnt[thread]; + cent.width = tiltcnt->imgs.width; + cent.height = tiltcnt->imgs.height; + cent.x = 0; + cent.y = atoi(tiltvalue); + // Add the number of frame to skip for motion detection + cnt[thread]->moved = track_move(tiltcnt, tiltcnt->video_dev, ¢, &tiltcnt->imgs, 1); + if (cnt[thread]->moved) { + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

" + "Thread %hu
\n" + "track set relative tilt=%s\n", + thread, thread, tiltvalue); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "track set relative tilt=%s\nDone\n", tiltvalue); + send_template_raw(client_socket, res); + } + } else { + /*error in track action*/ + if (cnt[0]->conf.webcontrol_html_output) { + sprintf(res, "<– back

" + "Thread %hu\n", thread, thread); + response_client(client_socket, track_error, res); + } else { + response_client(client_socket, track_error_raw, NULL); + } + } + } else if (X) { + /* X */ + setcnt = cnt[thread]; + // 1000 is out of range for pwc + cnt[thread]->moved = track_center(setcnt, setcnt->video_dev, 1, atoi(x_value), 1000); + if (cnt[thread]->moved) { + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

" + "Thread %hu
\n" + "track set absolute x=%s\n", + thread, thread, x_value); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "track set absolute x=%s\nDone\n", x_value); + send_template_raw(client_socket, res); + } + } else { + /*error in track action*/ + if (cnt[0]->conf.webcontrol_html_output) { + sprintf(res, "<– back

" + "Thread %hu\n", thread, thread); + response_client(client_socket, track_error, res); + } else { + response_client(client_socket, track_error_raw, NULL); + } + } + + } else { + /* Y */ + setcnt = cnt[thread]; + // 1000 is out of range for pwc + cnt[thread]->moved = track_center(setcnt, setcnt->video_dev, 1, 1000, atoi(y_value)); + if (cnt[thread]->moved) { + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

" + "Thread %hu
\n" + "track set absolute y=%s
\n", + thread, thread, y_value); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "track set absolute y=%s\nDone\n", y_value); + send_template_raw(client_socket, res); + } + } else { + /*error in track action*/ + if (cnt[0]->conf.webcontrol_html_output) { + sprintf(res, "<– back

" + "Thread %hu\n", thread, thread); + response_client(client_socket, track_error, res); + } else { + response_client(client_socket, track_error_raw, NULL); + } + } + } + return 1; + } + + + /* Check Second parameter */ + + warningkill = sscanf(pointer, "%c%255[a-z]" , &question, command); + if ((question != '&') || (command[0] == '\0')) { + motion_log(LOG_WARNING, 0, "%s: httpd debug race 4", __FUNCTION__); + if (strstr(pointer, "&")) { + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, error_value, NULL); + else + response_client(client_socket, error_value_raw, NULL); + + /* no valid syntax */ + } else { + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + } + return 1; + } + + pointer++; + length_uri--; + + if (!strcmp(command, "pan")) { + pointer = pointer + 3; + length_uri = length_uri - 3; + if (pan || !tilt || X || Y) { + motion_log(LOG_WARNING, 0, "%s: httpd debug race 5", __FUNCTION__); + /* no valid syntax */ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + return 1; + } + pan=2; + warningkill = sscanf(pointer, "%c%10[-0-9]" , &question, panvalue); + + } else if (!strcmp(command, "tilt")) { + pointer = pointer + 4; + length_uri = length_uri - 4; + if (tilt || !pan || X || Y) { + /* no valid syntax */ + motion_log(LOG_WARNING, 0, "%s: httpd debug race 6", __FUNCTION__); + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + return 1; + } + tilt = 2; + warningkill = sscanf(pointer, "%c%10[-0-9]" , &question, tiltvalue); + + } else if (!strcmp(command, "x")) { + pointer++; + length_uri--; + if (X || !Y || pan || tilt) { + motion_log(LOG_WARNING, 0, "%s: httpd debug race 7", __FUNCTION__); + + /* no valid syntax */ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + return 1; + } + X = 2; + warningkill = sscanf(pointer, "%c%10[-0-9]" , &question, x_value); + + } else if (!strcmp(command, "y")) { + pointer++; + length_uri--; + if (Y || !X || pan || tilt) { + motion_log(LOG_WARNING, 0, "%s: httpd debug race 8", __FUNCTION__); + /* no valid syntax */ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + return 1; + } + Y = 2; + warningkill = sscanf(pointer, "%c%10[-0-9]" , &question, y_value); + + } else { + motion_log(LOG_WARNING, 0, "%s: httpd debug race 9", __FUNCTION__); + /* no valid syntax */ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + return 1; + } + + /* Second value check */ + + if ((warningkill < 2) && (question != '=')) { + motion_log(LOG_WARNING, 0, "%s: httpd debug race 10", __FUNCTION__); + /* no valid syntax */ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_valid_syntax, NULL); + else + response_client(client_socket, not_valid_syntax_raw, NULL); + return 1; + } else if ((question == '=') && (warningkill == 1)) { + motion_log(LOG_WARNING, 0, "%s: httpd debug race 11", __FUNCTION__); + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, error_value, NULL); + else + response_client(client_socket, error_value_raw, NULL); + return 1; + } + + + if (pan == 2) { + pointer = pointer + strlen(panvalue) + 1; + length_uri = length_uri - strlen(panvalue) - 1; + } else if (tilt == 2) { + pointer = pointer + strlen(tiltvalue) + 1; + length_uri = length_uri - strlen(tiltvalue) - 1; + } else if (X == 2) { + pointer = pointer + strlen(x_value) + 1; + length_uri = length_uri - strlen(x_value) - 1; + } else { + pointer = pointer + strlen(y_value) + 1; + length_uri = length_uri - strlen(y_value) - 1; + } + + + if (length_uri != 0) { + motion_log(LOG_WARNING, 0, "%s: httpd debug race 12", __FUNCTION__); + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, error_value, NULL); + else + response_client(client_socket, error_value_raw, NULL); + return 1; + } + + + /* track set absolute ( x , y )*/ + + if (X && Y) { + setcnt = cnt[thread]; + cnt[thread]->moved = track_center(setcnt, setcnt->video_dev, 1, atoi(x_value), atoi(y_value)); + if (cnt[thread]->moved) { + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

" + "Thread %hu
\n" + "track absolute set x=%s y=%s
\n", + thread, thread, x_value, y_value); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "track absolute set x=%s y=%s\nDone\n", x_value, y_value); + send_template_raw(client_socket, res); + } + } else { + /*error in track action*/ + if (cnt[0]->conf.webcontrol_html_output) { + sprintf(res, "<– back

" + "Thread %hu\n", thread, thread); + response_client(client_socket, track_error, res); + } else { + response_client(client_socket, track_error_raw, NULL); + } + } + /* track set relative ( pan , tilt )*/ + } else { + struct coord cent; + struct context *relativecnt; + + /* move pan */ + + relativecnt = cnt[thread]; + cent.width = relativecnt->imgs.width; + cent.height = relativecnt->imgs.height; + cent.y = 0; + cent.x = atoi(panvalue); + // Add the number of frame to skip for motion detection + cnt[thread]->moved = track_move(relativecnt, relativecnt->video_dev, ¢, &relativecnt->imgs, 1); + + if (cnt[thread]->moved) { + /* move tilt */ + relativecnt = cnt[thread]; + cent.width = relativecnt->imgs.width; + cent.height = relativecnt->imgs.height; + cent.x = 0; + cent.y = atoi(tiltvalue); + // SLEEP(1,0); + cnt[thread]->moved = track_move(relativecnt, relativecnt->video_dev, ¢, &relativecnt->imgs, 1); + if (cnt[thread]->moved) { + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

" + "Thread %hu
\n" + "track relative pan=%s tilt=%s\n", + thread, thread, panvalue, tiltvalue); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "track relative pan=%s tilt=%s\nDone\n", panvalue, tiltvalue); + send_template_raw(client_socket, res); + } + return 1; + + } else { + /*error in track tilt*/ + if (cnt[0]->conf.webcontrol_html_output) { + sprintf(res, "<– back

" + "Thread %hu\n", thread, thread); + response_client(client_socket, track_error, res); + } else { + response_client(client_socket, track_error_raw, NULL); + } + } + } + + /*error in track pan*/ + if (cnt[0]->conf.webcontrol_html_output) { + sprintf(res, "<– back

Thread %hu\n", + thread, thread); + response_client(client_socket, track_error, res); + } else { + response_client(client_socket, track_error_raw, NULL); + } + } + } else if (length_uri == 0) { + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\nThread %hu
\n" + "
\n" + "Pan\n" + "Tilt\n" + "\n" + "
\n" + "
\n" + "X\n" + "Y\n" + "\n" + "
\n", thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "set needs a pan/tilt or x/y values\n"); + send_template_raw(client_socket, res); + } + } else { + /* error not valid command */ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (!strcmp(command, "center")) { + pointer = pointer+6; + length_uri = length_uri-6; + if (length_uri==0) { + struct context *setcnt; + setcnt = cnt[thread]; + // 1000 is out of range for pwc + cnt[thread]->moved = track_center(setcnt, setcnt->video_dev, 1, 0, 0); + + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\nThread %hu" + "
track set center", thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + sprintf(res, "Thread %hu\n track set center\nDone\n", thread); + send_template_ini_client_raw(client_socket); + send_template_raw(client_socket, res); + } + } else { + /* error not valid command */ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (!strcmp(command, "status")) { + pointer = pointer+6; + length_uri = length_uri-6; + if (length_uri==0) { + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\nThread %hu" + "
track auto %s", thread, thread, + (cnt[thread]->track.active)? "enabled":"disabled"); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + sprintf(res, "Thread %hu\n track auto %s\nDone\n", thread, + (cnt[thread]->track.active)? "enabled":"disabled"); + send_template_ini_client_raw(client_socket); + send_template_raw(client_socket, res); + } + } else { + /* error not valid command */ + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (!strcmp(command, "auto")) { + pointer = pointer + 4; + length_uri = length_uri - 4; + if ((question == '?') && (length_uri > 0)) { + char query[256] = {'\0'}; + pointer++; + length_uri--; + /* value= */ + + warningkill = sscanf(pointer, "%255[a-z]%c", query, &question); + if ((question == '=') && (!strcmp(query, "value"))) { + pointer = pointer + 6; + length_uri = length_uri - 6; + warningkill = sscanf(pointer, "%255[-0-9a-z]" , command); + if ((command != NULL) && (strlen(command) > 0)) { + struct context *autocnt; + + /* auto value=0|1|status*/ + + if (!strcmp(command, "status")) { + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

" + "Thread %hu
" + "track auto %s", thread, thread, + (cnt[thread]->track.active)? "enabled":"disabled"); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + sprintf(res, "Thread %hu\n track auto %s\nDone\n", thread, + (cnt[thread]->track.active)? "enabled":"disabled"); + send_template_ini_client_raw(client_socket); + send_template_raw(client_socket, res); + } + } else { + int active; + active = atoi(command); + /* CHECK */ + if (active > -1 && active < 2) { + autocnt = cnt[thread]; + autocnt->track.active = active; + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

" + "Thread %hu" + "
track auto %s
", thread, thread, + active ? "enabled":"disabled"); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "track auto %s\nDone\n", active ? "enabled":"disabled"); + send_template_raw(client_socket, res); + } + } else { + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } + } else { + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else { + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (length_uri == 0) { + + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\nThread %hu\n" + "
\n" + "
\n", thread, thread, (cnt[thread]->track.active) ? "selected":"", + (cnt[thread]->track.active) ? "selected":""); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "auto accepts only 0,1 or status as valid value\n"); + send_template_raw(client_socket, res); + } + } else { + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else { + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + + return 1; } /* - parses the action requested for motion ( config , action , detection , track ) and call - to action function if needed. + parses the action requested for motion ( config , action , detection , track ) and call + to action function if needed. - return 0 on action restart or quit - return 1 on success + return 0 on action restart or quit + return 1 on success */ static unsigned short int handle_get(int client_socket, const char *url, void *userdata) { - struct context **cnt = userdata; - if (*url == '/' ) { - unsigned short int i = 0; - char *res=NULL; - res = mymalloc(2048); - - /* get the number of threads */ - while (cnt[++i]); - /* ROOT_URI -> GET / */ - if (! (strcmp (url, "/")) ) { - unsigned short int y; - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "Motion "VERSION" Running [%hu] Threads
\n" - "All
\n", i); - send_template(client_socket, res); - for (y = 1; y < i; y++) { - sprintf(res, "Thread %hu
\n", y, y); - send_template(client_socket, res); - } - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "Motion "VERSION" Running [%hu] Threads\n0\n", i); - send_template_raw(client_socket, res); - for (y=1; y GET /2 */ - pointer++; - length_uri--; - warningkill = sscanf(pointer, "%hd%c", &thread, &slash); - - if ((thread != -1) && (thread < i)) { - /* thread_number found */ - if (thread > 9) { - pointer = pointer + 2; - length_uri = length_uri - 2; - }else{ - pointer++; - length_uri--; - } - - if (slash == '/') { /* slash found /2/ */ - pointer++; - length_uri--; - } - - if (length_uri!=0) { - warningkill = sscanf(pointer, "%255[a-z]%c" , command , &slash); - - /* config */ - if (!strcmp(command, "config")) { - pointer = pointer + 6; - length_uri = length_uri - 6; - if (length_uri == 0) { - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\n" - "Thread %hd
\n" - "list
\n" - "write
\n" - "set
\n" - "get
\n", - thread, thread, thread, thread, thread, thread); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "Thread %hd\nlist\nwrite\nset\nget\n", thread); - send_template_raw(client_socket, res); - } - } else if ((slash == '/') && (length_uri >= 4)) { - /*call config() */ - pointer++; - length_uri--; - config(pointer, res, length_uri, thread, client_socket, cnt); - } else { - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } - /* action */ - else if (!strcmp(command, "action")) { - pointer = pointer + 6; - length_uri = length_uri - 6; - /* call action() */ - if (length_uri == 0) { - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\n" - "Thread %hd
\n" - "makemovie
\n" - "snapshot
\n" - "restart
\n" - "quit
\n", - thread, thread, thread, thread, thread, thread); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "Thread %hd\nmakemovie\nsnapshot\nrestart\nquit\n", thread); - send_template_raw(client_socket, res); - } - } else if ((slash == '/') && (length_uri > 4)) { - unsigned short int ret = 1; - pointer++; - length_uri--; - ret = action(pointer, res, length_uri, thread, client_socket, cnt); - free(res); - return ret; - - } else { - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command,NULL); - else - response_client(client_socket, not_found_response_valid_command_raw,NULL); - } - } - /* detection */ - else if (!strcmp(command, "detection")) { - pointer = pointer + 9; - length_uri = length_uri - 9; - if (length_uri == 0) { - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\n" - "Thread %hd
\n" - "status
\n" - "start
\n" - "pause
\n" - "connection
\n", - thread, thread, thread, thread, thread, thread); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "Thread %hd\nstatus\nstart\npause\nconnection\n", thread); - send_template_raw(client_socket, res); - } - } else if ((slash == '/') && (length_uri > 5)) { - pointer++; - length_uri--; - /* call detection() */ - detection(pointer, res, length_uri, thread, client_socket, cnt); - } else { - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_valid_command, NULL); - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } - /* track */ - else if (!strcmp(command,"track")) { - pointer = pointer + 5; - length_uri = length_uri - 5; - if (length_uri == 0) { - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\n" - "Thread %hd
\n" - "track set pan/tilt
\n" - "track center
\n" - "track auto
\n" - "track status
\n", - thread, thread, thread, thread, thread, thread); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "Thread %hd\nset pan/tilt\ncenter\nauto\nstatus\n", thread); - send_template_raw(client_socket, res); - } - } - else if ((slash == '/') && (length_uri >= 4)) { - pointer++; - length_uri--; - /* call track() */ - if (cnt[thread]->track.type) { - track(pointer, res, length_uri, thread, client_socket, cnt); - } else { - /* error track not enable */ - if (cnt[0]->conf.webcontrol_html_output) { - sprintf(res, "<– back\n", thread); - response_client(client_socket, not_track, res); - } - else - response_client(client_socket, not_track_raw, NULL); - } - } else { - if (cnt[0]->conf.webcontrol_html_output) { - sprintf(res, "<– back\n", thread); - response_client(client_socket, not_found_response_valid_command, res); - } - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else { - if (cnt[0]->conf.webcontrol_html_output) { - sprintf(res, "<– back\n", thread); - response_client(client_socket, not_found_response_valid_command, res); - } - else - response_client(client_socket, not_found_response_valid_command_raw, NULL); - } - } else { - /* /thread_number/ requested */ - if (cnt[0]->conf.webcontrol_html_output) { - send_template_ini_client(client_socket, ini_template); - sprintf(res, "<– back

\nThread %hd
\n" - "config
\n" - "action
\n" - "detection
\n" - "track
\n", - thread, thread, thread, thread, thread); - send_template(client_socket, res); - send_template_end_client(client_socket); - } else { - send_template_ini_client_raw(client_socket); - sprintf(res, "Thread %hd\nconfig\naction\ndetection\ntrack\n", thread); - send_template_raw(client_socket, res); - } - } - } else { - if (cnt[0]->conf.webcontrol_html_output) { - sprintf(res, "<– back\n"); - response_client(client_socket, not_found_response_valid, res); - } - else - response_client(client_socket, not_found_response_valid_raw, NULL); - } - } - free(res); - } else { - if (cnt[0]->conf.webcontrol_html_output) - response_client(client_socket, not_found_response_template,NULL); - else - response_client(client_socket, not_found_response_template_raw,NULL); - } - - return 1; + struct context **cnt = userdata; + + if (*url == '/') { + unsigned short int i = 0; + char *res=NULL; + res = mymalloc(2048); + + /* get the number of threads */ + while (cnt[++i]); + /* ROOT_URI -> GET / */ + if (!strcmp(url, "/")) { + unsigned short int y; + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "Motion "VERSION" Running [%hu] Threads
\n" + "All
\n", i); + send_template(client_socket, res); + for (y = 1; y < i; y++) { + sprintf(res, "Thread %hu
\n", y, y); + send_template(client_socket, res); + } + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "Motion "VERSION" Running [%hu] Threads\n0\n", i); + send_template_raw(client_socket, res); + for (y = 1; y < i; y++) { + sprintf(res, "%hu\n", y); + send_template_raw(client_socket, res); + } + } + + } else { + char command[256] = {'\0'}; + char slash; + short int thread = -1; + size_t length_uri = 0; + char *pointer = (char *)url; + + length_uri = strlen(url); + /* Check for Thread number first -> GET /2 */ + pointer++; + length_uri--; + warningkill = sscanf(pointer, "%hd%c", &thread, &slash); + + if ((thread != -1) && (thread < i)) { + /* thread_number found */ + if (thread > 9) { + pointer = pointer + 2; + length_uri = length_uri - 2; + } else { + pointer++; + length_uri--; + } + + if (slash == '/') { /* slash found /2/ */ + pointer++; + length_uri--; + } + + if (length_uri != 0) { + warningkill = sscanf(pointer, "%255[a-z]%c" , command , &slash); + + /* config */ + if (!strcmp(command, "config")) { + pointer = pointer + 6; + length_uri = length_uri - 6; + if (length_uri == 0) { + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\n" + "Thread %hd
\n" + "list
\n" + "write
\n" + "set
\n" + "get
\n", + thread, thread, thread, thread, thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "Thread %hd\nlist\nwrite\nset\nget\n", thread); + send_template_raw(client_socket, res); + } + } else if ((slash == '/') && (length_uri >= 4)) { + /*call config() */ + pointer++; + length_uri--; + config(pointer, res, length_uri, thread, client_socket, cnt); + } else { + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + + } else if (!strcmp(command, "action")) { /* action */ + pointer = pointer + 6; + length_uri = length_uri - 6; + /* call action() */ + if (length_uri == 0) { + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\n" + "Thread %hd
\n" + "makemovie
\n" + "snapshot
\n" + "restart
\n" + "quit
\n", + thread, thread, thread, thread, thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "Thread %hd\nmakemovie\nsnapshot\nrestart\nquit\n", thread); + send_template_raw(client_socket, res); + } + } else if ((slash == '/') && (length_uri > 4)) { + unsigned short int ret = 1; + pointer++; + length_uri--; + ret = action(pointer, res, length_uri, thread, client_socket, cnt); + free(res); + return ret; + + } else { + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command,NULL); + else + response_client(client_socket, not_found_response_valid_command_raw,NULL); + } + } else if (!strcmp(command, "detection")) { /* detection */ + pointer = pointer + 9; + length_uri = length_uri - 9; + if (length_uri == 0) { + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\n" + "Thread %hd
\n" + "status
\n" + "start
\n" + "pause
\n" + "connection
\n", + thread, thread, thread, thread, thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "Thread %hd\nstatus\nstart\npause\nconnection\n", thread); + send_template_raw(client_socket, res); + } + } else if ((slash == '/') && (length_uri > 5)) { + pointer++; + length_uri--; + /* call detection() */ + detection(pointer, res, length_uri, thread, client_socket, cnt); + } else { + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_valid_command, NULL); + else + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } else if (!strcmp(command,"track")) { /* track */ + pointer = pointer + 5; + length_uri = length_uri - 5; + if (length_uri == 0) { + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\n" + "Thread %hd
\n" + "track set pan/tilt
\n" + "track center
\n" + "track auto
\n" + "track status
\n", + thread, thread, thread, thread, thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "Thread %hd\nset pan/tilt\ncenter\nauto\nstatus\n", thread); + send_template_raw(client_socket, res); + } + + } else if ((slash == '/') && (length_uri >= 4)) { + pointer++; + length_uri--; + /* call track() */ + if (cnt[thread]->track.type) { + track(pointer, res, length_uri, thread, client_socket, cnt); + } else { + /* error track not enable */ + if (cnt[0]->conf.webcontrol_html_output) { + sprintf(res, "<– back\n", thread); + response_client(client_socket, not_track, res); + } else { + response_client(client_socket, not_track_raw, NULL); + } + } + } else { + if (cnt[0]->conf.webcontrol_html_output) { + sprintf(res, "<– back\n", thread); + response_client(client_socket, not_found_response_valid_command, res); + } else { + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } + } else { + if (cnt[0]->conf.webcontrol_html_output) { + sprintf(res, "<– back\n", thread); + response_client(client_socket, not_found_response_valid_command, res); + } else { + response_client(client_socket, not_found_response_valid_command_raw, NULL); + } + } + } else { + /* /thread_number/ requested */ + if (cnt[0]->conf.webcontrol_html_output) { + send_template_ini_client(client_socket, ini_template); + sprintf(res, "<– back

\nThread %hd
\n" + "config
\n" + "action
\n" + "detection
\n" + "track
\n", + thread, thread, thread, thread, thread); + send_template(client_socket, res); + send_template_end_client(client_socket); + } else { + send_template_ini_client_raw(client_socket); + sprintf(res, "Thread %hd\nconfig\naction\ndetection\ntrack\n", thread); + send_template_raw(client_socket, res); + } + } + } else { + if (cnt[0]->conf.webcontrol_html_output) { + sprintf(res, "<– back\n"); + response_client(client_socket, not_found_response_valid, res); + } else { + response_client(client_socket, not_found_response_valid_raw, NULL); + } + } + } + free(res); + } else { + if (cnt[0]->conf.webcontrol_html_output) + response_client(client_socket, not_found_response_template,NULL); + else + response_client(client_socket, not_found_response_template_raw,NULL); + } + + return 1; } @@ -2170,150 +2171,141 @@ static unsigned short int handle_get(int client_socket, const char *url, void *u static unsigned short int read_client(int client_socket, void *userdata, char *auth) { - unsigned short int alive = 1; - unsigned short int ret = 1; - char buffer[1024] = {'\0'}; - ssize_t length = 1023; - struct context **cnt = userdata; - - /* lock the mutex */ - pthread_mutex_lock(&httpd_mutex); - - while (alive) - { - ssize_t nread = 0, readb = -1; - - nread = read_nonblock(client_socket, buffer, length); - - if (nread <= 0) { - motion_log(LOG_ERR, 1, "%s: motion-httpd First Read Error", __FUNCTION__); - pthread_mutex_unlock(&httpd_mutex); - return 1; - } - else { - char method[10]={'\0'}; - char url[512]={'\0'}; - char protocol[10]={'\0'}; - char *authentication=NULL; - - buffer[nread] = '\0'; - - warningkill = sscanf(buffer, "%9s %511s %9s", method, url, protocol); - - if ( warningkill != 3 ) { - if (cnt[0]->conf.webcontrol_html_output) - warningkill = write_nonblock(client_socket, bad_request_response, - sizeof (bad_request_response)); - else - warningkill = write_nonblock(client_socket, bad_request_response_raw, - sizeof (bad_request_response_raw)); - pthread_mutex_unlock(&httpd_mutex); - return 1; - } - - while ((strstr(buffer, "\r\n\r\n") == NULL) && (readb != 0) && (nread < length)) { - readb = read_nonblock(client_socket, buffer+nread, sizeof (buffer) - nread); - - if (readb == -1) { - nread = -1; - break; - } - - nread += readb; - - if (nread > length) { - motion_log(LOG_ERR, 1, "%s: motion-httpd End buffer reached waiting " - "for buffer ending", __FUNCTION__); - break; - } - buffer[nread] = '\0'; - } - - /* Make sure the last read didn't fail. If it did, there's a - problem with the connection, so give up. */ - if (nread == -1) { - motion_log(LOG_ERR, 1, "%s: motion-httpd READ give up!", __FUNCTION__); - pthread_mutex_unlock(&httpd_mutex); - return 1; - } - alive = 0; - - /* Check Protocol */ - if (strcmp(protocol, "HTTP/1.0") && strcmp (protocol, "HTTP/1.1")) { - /* We don't understand this protocol. Report a bad response. */ - if (cnt[0]->conf.webcontrol_html_output) - warningkill = write_nonblock(client_socket, bad_request_response, - sizeof (bad_request_response)); - else - warningkill = write_nonblock(client_socket, bad_request_response_raw, - sizeof (bad_request_response_raw)); - - pthread_mutex_unlock(&httpd_mutex); - return 1; - } - - if (strcmp (method, "GET")) { - /* This server only implements the GET method. If client - uses other method, report the failure. */ - char response[1024]; - if (cnt[0]->conf.webcontrol_html_output) - snprintf(response, sizeof (response), bad_method_response_template, method); - else - snprintf(response, sizeof (response), bad_method_response_template_raw, method); - warningkill = write_nonblock(client_socket, response, strlen (response)); - pthread_mutex_unlock(&httpd_mutex); - return 1; - } - - if ( auth != NULL) { - if ( (authentication = strstr(buffer,"Basic")) ) { - char *end_auth = NULL; - authentication = authentication + 6; - - if ( (end_auth = strstr(authentication,"\r\n")) ) { - authentication[end_auth - authentication] = '\0'; - } else { - char response[1024]; - snprintf(response, sizeof (response), request_auth_response_template, method); - warningkill = write_nonblock(client_socket, response, strlen (response)); - pthread_mutex_unlock(&httpd_mutex); - return 1; - } - - // Don't allow to change webcontrol_authentication from http control - // If it has to be allowed reenable check_authentication() - /*if ( !check_authentication(auth, authentication, - strlen(cnt[0]->conf.webcontrol_authentication), - cnt[0]->conf.webcontrol_authentication)) { - */ - - if (strcmp(auth, authentication)) { - char response[1024] = {'\0'}; - snprintf(response, sizeof (response), request_auth_response_template, method); - warningkill = write_nonblock(client_socket, response, strlen (response)); - pthread_mutex_unlock(&httpd_mutex); - return 1; - } else { - ret = handle_get(client_socket, url, cnt); - /* A valid auth request. Process it. */ - } - } else { - // Request Authorization - char response[1024] = {'\0'}; - snprintf(response, sizeof (response), request_auth_response_template, method); - warningkill = write_nonblock(client_socket, response, strlen (response)); - pthread_mutex_unlock(&httpd_mutex); - return 1; - } - } else { - ret = handle_get(client_socket, url, cnt); - /* A valid request. Process it. */ - } - } - } - pthread_mutex_unlock(&httpd_mutex); - - return ret; + unsigned short int alive = 1; + unsigned short int ret = 1; + char buffer[1024] = {'\0'}; + ssize_t length = 1023; + struct context **cnt = userdata; + + /* lock the mutex */ + pthread_mutex_lock(&httpd_mutex); + + while (alive) { + ssize_t nread = 0, readb = -1; + + nread = read_nonblock(client_socket, buffer, length); + + if (nread <= 0) { + motion_log(LOG_ERR, 1, "%s: motion-httpd First Read Error", __FUNCTION__); + pthread_mutex_unlock(&httpd_mutex); + return 1; + } else { + char method[10]={'\0'}; + char url[512]={'\0'}; + char protocol[10]={'\0'}; + char *authentication=NULL; + + buffer[nread] = '\0'; + + warningkill = sscanf(buffer, "%9s %511s %9s", method, url, protocol); + + if (warningkill != 3) { + if (cnt[0]->conf.webcontrol_html_output) + warningkill = write_nonblock(client_socket, bad_request_response, + sizeof (bad_request_response)); + else + warningkill = write_nonblock(client_socket, bad_request_response_raw, + sizeof (bad_request_response_raw)); + pthread_mutex_unlock(&httpd_mutex); + return 1; + } + + while ((strstr(buffer, "\r\n\r\n") == NULL) && (readb != 0) && (nread < length)) { + readb = read_nonblock(client_socket, buffer+nread, sizeof (buffer) - nread); + + if (readb == -1) { + nread = -1; + break; + } + + nread += readb; + + if (nread > length) { + motion_log(LOG_ERR, 1, "%s: motion-httpd End buffer reached waiting " + "for buffer ending", __FUNCTION__); + break; + } + buffer[nread] = '\0'; + } + + /* Make sure the last read didn't fail. If it did, there's a + problem with the connection, so give up. */ + if (nread == -1) { + motion_log(LOG_ERR, 1, "%s: motion-httpd READ give up!", __FUNCTION__); + pthread_mutex_unlock(&httpd_mutex); + return 1; + } + alive = 0; + + /* Check Protocol */ + if (strcmp(protocol, "HTTP/1.0") && strcmp (protocol, "HTTP/1.1")) { + /* We don't understand this protocol. Report a bad response. */ + if (cnt[0]->conf.webcontrol_html_output) + warningkill = write_nonblock(client_socket, bad_request_response, + sizeof (bad_request_response)); + else + warningkill = write_nonblock(client_socket, bad_request_response_raw, + sizeof (bad_request_response_raw)); + + pthread_mutex_unlock(&httpd_mutex); + return 1; + } + + if (strcmp (method, "GET")) { + /* This server only implements the GET method. If client + uses other method, report the failure. */ + char response[1024]; + if (cnt[0]->conf.webcontrol_html_output) + snprintf(response, sizeof (response), bad_method_response_template, method); + else + snprintf(response, sizeof (response), bad_method_response_template_raw, method); + warningkill = write_nonblock(client_socket, response, strlen (response)); + pthread_mutex_unlock(&httpd_mutex); + return 1; + } + + if (auth != NULL) { + if ((authentication = strstr(buffer,"Basic"))) { + char *end_auth = NULL; + authentication = authentication + 6; + + if ((end_auth = strstr(authentication,"\r\n"))) { + authentication[end_auth - authentication] = '\0'; + } else { + char response[1024]; + snprintf(response, sizeof (response), request_auth_response_template, method); + warningkill = write_nonblock(client_socket, response, strlen (response)); + pthread_mutex_unlock(&httpd_mutex); + return 1; + } + + if (strcmp(auth, authentication)) { + char response[1024] = {'\0'}; + snprintf(response, sizeof (response), request_auth_response_template, method); + warningkill = write_nonblock(client_socket, response, strlen (response)); + pthread_mutex_unlock(&httpd_mutex); + return 1; + } else { + ret = handle_get(client_socket, url, cnt); + /* A valid auth request. Process it. */ + } + } else { + // Request Authorization + char response[1024] = {'\0'}; + snprintf(response, sizeof (response), request_auth_response_template, method); + warningkill = write_nonblock(client_socket, response, strlen (response)); + pthread_mutex_unlock(&httpd_mutex); + return 1; + } + } else { + ret = handle_get(client_socket, url, cnt); + /* A valid request. Process it. */ + } + } + } + pthread_mutex_unlock(&httpd_mutex); + + return ret; } @@ -2322,32 +2314,32 @@ static unsigned short int read_client(int client_socket, void *userdata, char *a This function waits timeout seconds for listen socket. Returns : - -1 if the timeout expires or on accept error. - curfd (client socket) on accept success. + -1 if the timeout expires or on accept error. + curfd (client socket) on accept success. */ static int acceptnonblocking(int serverfd, int timeout) { - int curfd; - struct sockaddr_storage client; - socklen_t namelen = sizeof(client); - - struct timeval tm; - fd_set fds; - - tm.tv_sec = timeout; /* Timeout in seconds */ - tm.tv_usec = 0; - FD_ZERO(&fds); - FD_SET(serverfd, &fds); - - if ( select (serverfd + 1, &fds, NULL, NULL, &tm) > 0) { - if(FD_ISSET(serverfd, &fds)) { - if((curfd = accept(serverfd, (struct sockaddr*)&client, &namelen)) > 0) - return(curfd); - } - } - - return -1; + int curfd; + struct sockaddr_storage client; + socklen_t namelen = sizeof(client); + + struct timeval tm; + fd_set fds; + + tm.tv_sec = timeout; /* Timeout in seconds */ + tm.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(serverfd, &fds); + + if (select(serverfd + 1, &fds, NULL, NULL, &tm) > 0) { + if (FD_ISSET(serverfd, &fds)) { + if ((curfd = accept(serverfd, (struct sockaddr*)&client, &namelen)) > 0) + return(curfd); + } + } + + return -1; } @@ -2357,145 +2349,145 @@ static int acceptnonblocking(int serverfd, int timeout) void httpd_run(struct context **cnt) { - int sd = -1, client_socket_fd, val; - unsigned short int client_sent_quit_message = 1, closehttpd = 0; - struct addrinfo hints, *res, *ressave; - struct sigaction act; - char *authentication = NULL; - char portnumber[10], hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; - - /* Initialize the mutex */ - pthread_mutex_init(&httpd_mutex, NULL); - - /* set signal handlers TO IGNORE */ - memset(&act, 0, sizeof(act)); - sigemptyset(&act.sa_mask); - act.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &act, NULL); - sigaction(SIGCHLD, &act, NULL); - - memset(&hints, 0, sizeof(struct addrinfo)); - /* AI_PASSIVE as we are going to listen */ - hints.ai_flags = AI_PASSIVE; + int sd = -1, client_socket_fd, val; + unsigned short int client_sent_quit_message = 1, closehttpd = 0; + struct addrinfo hints, *res, *ressave; + struct sigaction act; + char *authentication = NULL; + char portnumber[10], hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; + + /* Initialize the mutex */ + pthread_mutex_init(&httpd_mutex, NULL); + + /* set signal handlers TO IGNORE */ + memset(&act, 0, sizeof(act)); + sigemptyset(&act.sa_mask); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, NULL); + sigaction(SIGCHLD, &act, NULL); + + memset(&hints, 0, sizeof(struct addrinfo)); + /* AI_PASSIVE as we are going to listen */ + hints.ai_flags = AI_PASSIVE; #if defined(BSD) - hints.ai_family = AF_INET; + hints.ai_family = AF_INET; #else - hints.ai_family = AF_UNSPEC; + hints.ai_family = AF_UNSPEC; #endif - hints.ai_socktype = SOCK_STREAM; - - snprintf(portnumber, sizeof(portnumber), "%u", cnt[0]->conf.webcontrol_port); - - val = getaddrinfo(cnt[0]->conf.webcontrol_localhost ? "localhost" : NULL, portnumber, &hints, &res); - - /* check != 0 to allow FreeBSD compatibility */ - if (val != 0) { - motion_log(LOG_ERR, 1, "%s: getaddrinfo() for httpd socket failed: %s", - __FUNCTION__, gai_strerror(val)); - if (res) - freeaddrinfo(res); - pthread_mutex_destroy(&httpd_mutex); - return; - } - - ressave = res; - - while (res) { - /* create socket */ - sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - - getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, - sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); - - motion_log(LOG_INFO, 0, "%s: motion-httpd testing : %s addr: %s port: %s", - __FUNCTION__, res->ai_family == AF_INET ? "IPV4":"IPV6", hbuf, sbuf); - - if (sd >= 0) { - val = 1; - /* Reuse Address */ - setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof( int ) ); - - if (bind(sd, res->ai_addr, res->ai_addrlen) == 0) { - motion_log(LOG_INFO, 0, "%s: motion-httpd Bound : %s addr: %s port: %s", - __FUNCTION__, res->ai_family == AF_INET ? "IPV4":"IPV6", hbuf, sbuf); - break; - } - - motion_log(LOG_ERR, 1, "%s: motion-httpd failed bind() interface %s / port %s, retrying", - __FUNCTION__, hbuf, sbuf); - close(sd); - sd = -1; - } - motion_log(LOG_ERR, 1, "%s: motion-httpd socket failed interface %s / port %s, retrying", - __FUNCTION__, hbuf, sbuf); - res = res->ai_next; - } - - freeaddrinfo(ressave); - - if (sd < 0) { - motion_log(LOG_ERR, 1, "%s: motion-httpd ERROR bind() [interface %s port %s]", - __FUNCTION__, hbuf, sbuf); - pthread_mutex_destroy(&httpd_mutex); - return; - } - - if (listen(sd,5) == -1) { - motion_log(LOG_ERR, 1, "%s: motion-httpd ERROR listen() [interface %s port %s]", - __FUNCTION__, hbuf, sbuf); - close(sd); - pthread_mutex_destroy(&httpd_mutex); - return; - } - - motion_log(LOG_DEBUG, 0, "%s: motion-httpd/"VERSION" running, accepting connections", __FUNCTION__); - motion_log(LOG_DEBUG, 0, "%s: motion-httpd: waiting for data on %s port TCP %s", __FUNCTION__, hbuf, sbuf); - - if (cnt[0]->conf.webcontrol_authentication != NULL) { - char *userpass = NULL; - size_t auth_size = strlen(cnt[0]->conf.webcontrol_authentication); - - authentication = (char *) mymalloc(BASE64_LENGTH(auth_size) + 1); - userpass = mymalloc(auth_size + 4); - /* base64_encode can read 3 bytes after the end of the string, initialize it */ - memset(userpass, 0, auth_size + 4); - strcpy(userpass, cnt[0]->conf.webcontrol_authentication); - base64_encode(userpass, authentication, auth_size); - free(userpass); - } - - while ((client_sent_quit_message) && (!closehttpd)) { - - client_socket_fd = acceptnonblocking(sd, 1); - - if (client_socket_fd < 0) { - if ((!cnt[0]) || (cnt[0]->finish)) { - motion_log(-1, 0, "%s: motion-httpd - Finishing", __FUNCTION__); - closehttpd = 1; - } - } else { - /* Get the Client request */ - client_sent_quit_message = read_client (client_socket_fd, cnt, authentication); - motion_log(-1, 0, "%s: motion-httpd - Read from client", __FUNCTION__); - - /* Close Connection */ - if (client_socket_fd) - close(client_socket_fd); - } - - } - - if (authentication != NULL) - free(authentication); - close(sd); - motion_log(LOG_DEBUG, 0, "%s: motion-httpd Closing", __FUNCTION__); - pthread_mutex_destroy(&httpd_mutex); + hints.ai_socktype = SOCK_STREAM; + + snprintf(portnumber, sizeof(portnumber), "%u", cnt[0]->conf.webcontrol_port); + + val = getaddrinfo(cnt[0]->conf.webcontrol_localhost ? "localhost" : NULL, portnumber, &hints, &res); + + /* check != 0 to allow FreeBSD compatibility */ + if (val != 0) { + motion_log(LOG_ERR, 1, "%s: getaddrinfo() for httpd socket failed: %s", + __FUNCTION__, gai_strerror(val)); + if (res) + freeaddrinfo(res); + pthread_mutex_destroy(&httpd_mutex); + return; + } + + ressave = res; + + while (res) { + /* create socket */ + sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + + getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, + sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); + + motion_log(LOG_INFO, 0, "%s: motion-httpd testing : %s addr: %s port: %s", + __FUNCTION__, res->ai_family == AF_INET ? "IPV4":"IPV6", hbuf, sbuf); + + if (sd >= 0) { + val = 1; + /* Reuse Address */ + setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int)); + + if (bind(sd, res->ai_addr, res->ai_addrlen) == 0) { + motion_log(LOG_INFO, 0, "%s: motion-httpd Bound : %s addr: %s port: %s", + __FUNCTION__, res->ai_family == AF_INET ? "IPV4":"IPV6", hbuf, sbuf); + break; + } + + motion_log(LOG_ERR, 1, "%s: motion-httpd failed bind() interface %s / port %s, retrying", + __FUNCTION__, hbuf, sbuf); + close(sd); + sd = -1; + } + motion_log(LOG_ERR, 1, "%s: motion-httpd socket failed interface %s / port %s, retrying", + __FUNCTION__, hbuf, sbuf); + res = res->ai_next; + } + + freeaddrinfo(ressave); + + if (sd < 0) { + motion_log(LOG_ERR, 1, "%s: motion-httpd ERROR bind() [interface %s port %s]", + __FUNCTION__, hbuf, sbuf); + pthread_mutex_destroy(&httpd_mutex); + return; + } + + if (listen(sd,5) == -1) { + motion_log(LOG_ERR, 1, "%s: motion-httpd ERROR listen() [interface %s port %s]", + __FUNCTION__, hbuf, sbuf); + close(sd); + pthread_mutex_destroy(&httpd_mutex); + return; + } + + motion_log(LOG_DEBUG, 0, "%s: motion-httpd/"VERSION" running, accepting connections", __FUNCTION__); + motion_log(LOG_DEBUG, 0, "%s: motion-httpd: waiting for data on %s port TCP %s", __FUNCTION__, hbuf, sbuf); + + if (cnt[0]->conf.webcontrol_authentication != NULL) { + char *userpass = NULL; + size_t auth_size = strlen(cnt[0]->conf.webcontrol_authentication); + + authentication = (char *) mymalloc(BASE64_LENGTH(auth_size) + 1); + userpass = mymalloc(auth_size + 4); + /* base64_encode can read 3 bytes after the end of the string, initialize it */ + memset(userpass, 0, auth_size + 4); + strcpy(userpass, cnt[0]->conf.webcontrol_authentication); + base64_encode(userpass, authentication, auth_size); + free(userpass); + } + + while ((client_sent_quit_message) && (!closehttpd)) { + + client_socket_fd = acceptnonblocking(sd, 1); + + if (client_socket_fd < 0) { + if ((!cnt[0]) || (cnt[0]->finish)) { + motion_log(-1, 0, "%s: motion-httpd - Finishing", __FUNCTION__); + closehttpd = 1; + } + } else { + /* Get the Client request */ + client_sent_quit_message = read_client(client_socket_fd, cnt, authentication); + motion_log(-1, 0, "%s: motion-httpd - Read from client", __FUNCTION__); + + /* Close Connection */ + if (client_socket_fd) + close(client_socket_fd); + } + + } + + if (authentication != NULL) + free(authentication); + close(sd); + motion_log(LOG_DEBUG, 0, "%s: motion-httpd Closing", __FUNCTION__); + pthread_mutex_destroy(&httpd_mutex); } void *motion_web_control(void *arg) { - struct context **cnt = arg; - httpd_run(cnt); - motion_log(LOG_DEBUG, 0, "%s: motion-httpd thread exit", __FUNCTION__); - pthread_exit(NULL); + struct context **cnt = arg; + httpd_run(cnt); + motion_log(LOG_DEBUG, 0, "%s: motion-httpd thread exit", __FUNCTION__); + pthread_exit(NULL); }