Add support for short movies
Short movies can now be recorded (~30 seconds, depending on disk space), with audio. New button is added, for that, depending on state it shows "Rec" (you can record), "Stop" (you are recording) and "Busy" (movie is being converted).
This commit is contained in:

committed by
Martijn Braam

parent
fcba2b33c2
commit
1c9202c4cf
@@ -12,6 +12,7 @@
|
||||
#ifndef SYSCONFDIR
|
||||
#include "config.h"
|
||||
#endif
|
||||
#include "medianame.h"
|
||||
|
||||
#include "dcp.h"
|
||||
#include "gl_util.h"
|
||||
@@ -19,6 +20,7 @@
|
||||
#include <jpeglib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
static const float colormatrix_srgb[] = { 3.2409f, -1.5373f, -0.4986f,
|
||||
-0.9692f, 1.8759f, 0.0415f,
|
||||
@@ -27,7 +29,7 @@ static const float colormatrix_srgb[] = { 3.2409f, -1.5373f, -0.4986f,
|
||||
static MPPipeline *pipeline;
|
||||
mp_state_proc state_proc;
|
||||
|
||||
static char burst_dir[23];
|
||||
static char burst_dir[255];
|
||||
|
||||
static volatile bool is_capturing = false;
|
||||
static volatile int frames_processed = 0;
|
||||
@@ -41,7 +43,7 @@ static int output_buffer_height = -1;
|
||||
static bool flash_enabled;
|
||||
static int framecounter = 0;
|
||||
|
||||
static char capture_fname[255];
|
||||
static char capture_fname[255], movie_script[255];
|
||||
|
||||
static GSettings *settings;
|
||||
|
||||
@@ -113,10 +115,8 @@ mp_process_find_all_processors(GtkListStore *store)
|
||||
}
|
||||
|
||||
bool
|
||||
mp_process_find_processor(char *script)
|
||||
mp_process_find_processor(char *script, char *filename)
|
||||
{
|
||||
char filename[] = "postprocess.sh";
|
||||
|
||||
// Check postprocess.sh in the current working directory
|
||||
sprintf(script, "./data/%s", filename);
|
||||
if (access(script, F_OK) != -1) {
|
||||
@@ -149,6 +149,20 @@ mp_process_find_processor(char *script)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void setup_capture(void)
|
||||
{
|
||||
char template[] = "/tmp/megapixels.XXXXXX";
|
||||
char *tempdir;
|
||||
tempdir = mkdtemp(template);
|
||||
|
||||
if (tempdir == NULL) {
|
||||
g_printerr("Could not make capture directory %s\n", template);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
strcpy(burst_dir, tempdir);
|
||||
}
|
||||
|
||||
static void
|
||||
setup(MPPipeline *pipeline, const void *data)
|
||||
{
|
||||
@@ -159,6 +173,12 @@ setup(MPPipeline *pipeline, const void *data)
|
||||
state_proc.mode_balance = AAA_BY_POST;
|
||||
state_proc.mode_exposure = AAA_BY_V4L2_CONTROLS;
|
||||
state_proc.mode_focus = AAA_DISABLED;
|
||||
|
||||
if (!mp_process_find_processor(movie_script, "movie.sh")) {
|
||||
printf("movie.sh not found\n");
|
||||
exit(1);
|
||||
}
|
||||
setup_capture();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -582,10 +602,94 @@ process_image_for_preview(const uint8_t *image)
|
||||
}
|
||||
|
||||
static void
|
||||
process_image_for_capture_yuv(const uint8_t *image, int count)
|
||||
format_timestamp(char *timestamp)
|
||||
{
|
||||
static char capture_fname[255];
|
||||
time_t rawtime;
|
||||
time(&rawtime);
|
||||
struct tm tim = *(localtime(&rawtime));
|
||||
|
||||
strftime(timestamp, 30, "%Y%m%d%H%M%S", &tim);
|
||||
}
|
||||
|
||||
static void
|
||||
format_movie_name(char *capture_fname)
|
||||
{
|
||||
char timestamp[30];
|
||||
format_timestamp(timestamp);
|
||||
|
||||
if (g_get_user_special_dir(G_USER_DIRECTORY_VIDEOS) != NULL) {
|
||||
sprintf(capture_fname,
|
||||
"%s/VID%s.mp4",
|
||||
g_get_user_special_dir(G_USER_DIRECTORY_VIDEOS),
|
||||
timestamp);
|
||||
} else if (getenv("XDG_VIDOES_DIR") != NULL) {
|
||||
sprintf(capture_fname,
|
||||
"%s/VID%s.mp4",
|
||||
getenv("XDG_VIDEOS_DIR"),
|
||||
timestamp);
|
||||
} else {
|
||||
sprintf(capture_fname,
|
||||
"%s/Videos/VID%s.mp4",
|
||||
getenv("HOME"),
|
||||
timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
int movie_recording;
|
||||
static char movie_fname[255];
|
||||
|
||||
static void
|
||||
on_movie_finished(GSubprocess *proc, GAsyncResult *res, GdkTexture *thumb)
|
||||
{
|
||||
notify_movie_progress();
|
||||
}
|
||||
|
||||
static void
|
||||
spawn_movie(char *cmd)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
GSubprocess *proc = g_subprocess_new(0,
|
||||
&error,
|
||||
movie_script,
|
||||
cmd,
|
||||
burst_dir,
|
||||
movie_fname,
|
||||
"305",
|
||||
NULL);
|
||||
|
||||
if (!proc) {
|
||||
g_printerr("Failed to spawn postprocess process: %s\n",
|
||||
error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
g_subprocess_communicate_utf8_async(
|
||||
proc, NULL, NULL, (GAsyncReadyCallback)on_movie_finished, NULL);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
on_movie_start(void)
|
||||
{
|
||||
format_movie_name(movie_fname);
|
||||
|
||||
movie_recording = 1;
|
||||
printf("movie recording on\n");
|
||||
spawn_movie("start");
|
||||
}
|
||||
|
||||
void
|
||||
on_movie_stop(void)
|
||||
{
|
||||
movie_recording = 0;
|
||||
printf("movie recording off\n");
|
||||
spawn_movie("stop");
|
||||
}
|
||||
|
||||
static void
|
||||
save_jpeg(const uint8_t *image, char *fname)
|
||||
{
|
||||
char fname[255];
|
||||
sprintf(fname, "%s/%d.jpg", burst_dir, count);
|
||||
FILE *outfile;
|
||||
if ((outfile = fopen(fname, "wb")) == NULL) {
|
||||
g_printerr("jpeg open %s: error %d, %s\n",
|
||||
@@ -603,8 +707,9 @@ process_image_for_capture_yuv(const uint8_t *image, int count)
|
||||
jpeg_create_compress(&cinfo);
|
||||
jpeg_stdio_dest(&cinfo, outfile);
|
||||
|
||||
//printf("Saving jpeg, %d x %d\n", cinfo.image_width, cinfo.image_height);
|
||||
cinfo.image_width = state_proc.mode->width & -1;
|
||||
cinfo.image_height = state_proc.mode->height & -1;
|
||||
cinfo.image_height = state_proc.mode->height & -1; // FIXME?
|
||||
cinfo.input_components = 3;
|
||||
cinfo.in_color_space = JCS_YCbCr;
|
||||
jpeg_set_defaults(&cinfo);
|
||||
@@ -667,11 +772,17 @@ process_image_for_capture_yuv(const uint8_t *image, int count)
|
||||
}
|
||||
|
||||
static void
|
||||
process_image_for_capture_bayer(const uint8_t *image, int count)
|
||||
process_image_for_capture_yuv(const uint8_t *image, int count)
|
||||
{
|
||||
char fname[255];
|
||||
sprintf(fname, "%s/%d.dng", burst_dir, count);
|
||||
char fname[255];
|
||||
sprintf(fname, "%s/%d.jpeg", burst_dir, count);
|
||||
|
||||
save_jpeg(image, fname);
|
||||
}
|
||||
|
||||
static void
|
||||
save_dng(const uint8_t *image, char *fname, int count)
|
||||
{
|
||||
uint16_t orientation;
|
||||
if (state_proc.device_rotation == 0) {
|
||||
orientation = state_proc.mode->mirrored ?
|
||||
@@ -716,7 +827,7 @@ process_image_for_capture_bayer(const uint8_t *image, int count)
|
||||
libdng_set_exposure_program(&dng, LIBDNG_EXPOSUREPROGRAM_MANUAL);
|
||||
}
|
||||
|
||||
printf("Writing frame to %s\n", fname);
|
||||
//printf("Writing frame to %s, %d x %d\n", fname, state_proc.mode->width, state_proc.mode->height);
|
||||
libdng_write(&dng,
|
||||
fname,
|
||||
state_proc.mode->width,
|
||||
@@ -766,6 +877,15 @@ process_image_for_capture_bayer(const uint8_t *image, int count)
|
||||
*/
|
||||
}
|
||||
|
||||
static void
|
||||
process_image_for_capture_bayer(const uint8_t *image, int count)
|
||||
{
|
||||
char fname[255];
|
||||
sprintf(fname, "%s/%d.dng", burst_dir, count);
|
||||
|
||||
save_dng(image, fname, count);
|
||||
}
|
||||
|
||||
static void
|
||||
process_image_for_capture(const uint8_t *image, int count)
|
||||
{
|
||||
@@ -808,12 +928,10 @@ post_process_finished(GSubprocess *proc, GAsyncResult *res, GdkTexture *thumb)
|
||||
static void
|
||||
process_capture_burst(GdkTexture *thumb)
|
||||
{
|
||||
time_t rawtime;
|
||||
time(&rawtime);
|
||||
struct tm tim = *(localtime(&rawtime));
|
||||
static char capture_fname[255];
|
||||
|
||||
char timestamp[30];
|
||||
strftime(timestamp, 30, "%Y%m%d%H%M%S", &tim);
|
||||
char timestamp[30];
|
||||
format_timestamp(timestamp);
|
||||
|
||||
if (g_get_user_special_dir(G_USER_DIRECTORY_PICTURES) != NULL) {
|
||||
sprintf(capture_fname,
|
||||
@@ -885,6 +1003,12 @@ process_image(MPPipeline *pipeline, const MPBuffer *buffer)
|
||||
memcpy(image, buffer->data, size);
|
||||
mp_io_pipeline_release_buffer(buffer->index);
|
||||
|
||||
if (movie_recording) {
|
||||
char name[1024];
|
||||
get_name(name, burst_dir, "dng");
|
||||
save_dng(image, name, 1);
|
||||
}
|
||||
|
||||
MPZBarImage *zbar_image = mp_zbar_image_new(image,
|
||||
state_proc.mode->format,
|
||||
state_proc.mode->width,
|
||||
@@ -933,8 +1057,24 @@ process_image(MPPipeline *pipeline, const MPBuffer *buffer)
|
||||
void
|
||||
mp_process_pipeline_process_image(MPBuffer buffer)
|
||||
{
|
||||
#ifdef DEBUG_FPS
|
||||
static clock_t last, now;
|
||||
static int last_n, now_n;
|
||||
now_n++;
|
||||
now = clock();
|
||||
if (now - last > CLOCKS_PER_SEC * 10) {
|
||||
printf("period %fms -- %d -- %f fps\n",
|
||||
(float)(now - last) / CLOCKS_PER_SEC * 1000,
|
||||
now_n - last_n,
|
||||
((float) now_n - last_n) / ((now - last) / CLOCKS_PER_SEC));
|
||||
last = now;
|
||||
last_n = now_n;
|
||||
}
|
||||
#endif
|
||||
|
||||
// If we haven't processed the previous frame yet, drop this one
|
||||
if (frames_received != frames_processed && !is_capturing) {
|
||||
printf("Dropping frame\n");
|
||||
mp_io_pipeline_release_buffer(buffer.index);
|
||||
return;
|
||||
}
|
||||
@@ -950,16 +1090,7 @@ mp_process_pipeline_process_image(MPBuffer buffer)
|
||||
static void
|
||||
capture()
|
||||
{
|
||||
char template[] = "/tmp/megapixels.XXXXXX";
|
||||
char *tempdir;
|
||||
tempdir = mkdtemp(template);
|
||||
|
||||
if (tempdir == NULL) {
|
||||
g_printerr("Could not make capture directory %s\n", template);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
strcpy(burst_dir, tempdir);
|
||||
setup_capture();
|
||||
|
||||
state_proc.captures_remaining = state_proc.burst_length;
|
||||
state_proc.counter = 0;
|
||||
|
Reference in New Issue
Block a user