mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
Added a new mjpeg module for Mjpeg support.
The built-in mjpeg module allows you to record videos seamlessly. It will automatically compress the frame buffer using the extra space in the main ram. So... you don't have to pass it jpeg images. Gets 7 FPS at 320x240 while connected to the computer too (it has to compress the frame twice in this situation). Anyway, the module work like Gif.
This commit is contained in:
parent
acc35704d8
commit
5316b26d83
@ -140,10 +140,11 @@ OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/img/,\
|
||||
rgb2rgb_tab.o \
|
||||
median.o \
|
||||
point.o \
|
||||
rectangle.o \
|
||||
bmp.o \
|
||||
ppm.o \
|
||||
gif.o \
|
||||
rectangle.o \
|
||||
mjpeg.o \
|
||||
fast.o \
|
||||
freak.o \
|
||||
template.o \
|
||||
@ -161,6 +162,7 @@ OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/py/, \
|
||||
py_time.o \
|
||||
mlx90620.o \
|
||||
py_gif.o \
|
||||
py_mjpeg.o \
|
||||
)
|
||||
|
||||
ifeq ($(TARGET), OPENMV1)
|
||||
|
||||
@ -43,10 +43,11 @@ SRCS += $(addprefix img/, \
|
||||
rgb2rgb_tab.c \
|
||||
median.c \
|
||||
point.c \
|
||||
rectangle.c \
|
||||
bmp.c \
|
||||
ppm.c \
|
||||
gif.c \
|
||||
rectangle.c \
|
||||
mjpeg.c \
|
||||
fast.c \
|
||||
freak.c \
|
||||
template.c \
|
||||
@ -64,6 +65,7 @@ SRCS += $(addprefix py/, \
|
||||
py_time.c \
|
||||
mlx90620.c \
|
||||
py_gif.c \
|
||||
py_mjpeg.c \
|
||||
)
|
||||
|
||||
OBJS = $(addprefix $(BUILD)/, $(SRCS:.c=.o))
|
||||
|
||||
@ -54,7 +54,7 @@ void *fb_alloc0(uint32_t size)
|
||||
|
||||
void *fb_alloc_all(uint32_t *size)
|
||||
{
|
||||
int temp = pointer - ((char *) FB_PIXELS()) - sizeof(uint32_t);
|
||||
int32_t temp = pointer - ((char *) FB_PIXELS()) - sizeof(uint32_t);
|
||||
|
||||
if (temp < sizeof(uint32_t)) {
|
||||
*size = 0;
|
||||
|
||||
@ -173,6 +173,8 @@ extern const int8_t lab_table[196608];
|
||||
__typeof__ (img1) _img1 = (img1); \
|
||||
(_img0->w==_img1->w)&&(_img0->h==_img1->h)&&(_img0->bpp==_img1->bpp); })
|
||||
|
||||
#define IM_JPEG_QUALITY (50)
|
||||
|
||||
// Main Ram = 196,608 bytes
|
||||
// -
|
||||
// struct framebuffer = 20 bytes
|
||||
@ -434,6 +436,11 @@ void gif_open(FIL *fp, int width, int height, bool color, bool loop);
|
||||
void gif_add_frame(FIL *fp, image_t *img, uint16_t delay);
|
||||
void gif_close(FIL *fp);
|
||||
|
||||
/* MJPEG functions */
|
||||
void mjpeg_open(FIL *fp, int width, int height);
|
||||
void mjpeg_add_frame(FIL *fp, uint32_t *frames, uint32_t *bytes, image_t *img);
|
||||
void mjpeg_close(FIL *fp, uint32_t *frames, uint32_t *bytes, float fps);
|
||||
|
||||
/* Basic image functions */
|
||||
int imlib_get_pixel(image_t *img, int x, int y);
|
||||
void imlib_set_pixel(image_t *img, int x, int y, int p);
|
||||
|
||||
149
src/omv/img/mjpeg.c
Normal file
149
src/omv/img/mjpeg.c
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* A super simple MJPEG encoder.
|
||||
*
|
||||
*/
|
||||
#include "ff_wrapper.h"
|
||||
#include "imlib.h"
|
||||
#include "fb_alloc.h"
|
||||
|
||||
#define SIZE_OFFSET (1*4)
|
||||
#define MICROS_OFFSET (8*4)
|
||||
#define FRAMES_OFFSET (12*4)
|
||||
#define RATE_0_OFFSET (19*4)
|
||||
#define LENGTH_0_OFFSET (21*4)
|
||||
#define RATE_1_OFFSET (33*4)
|
||||
#define LENGTH_1_OFFSET (35*4)
|
||||
#define MOVI_OFFSET (54*4)
|
||||
|
||||
void mjpeg_open(FIL *fp, int width, int height)
|
||||
{
|
||||
write_data(fp, "RIFF", 4); // FOURCC fcc; - 0
|
||||
write_long(fp, 0); // DWORD cb; size - updated on close - 1
|
||||
write_data(fp, "AVI ", 4); // FOURCC fcc; - 2
|
||||
|
||||
write_data(fp, "LIST", 4); // FOURCC fcc; - 3
|
||||
write_long(fp, 192); // DWORD cb; - 4
|
||||
write_data(fp, "hdrl", 4); // FOURCC fcc; - 5
|
||||
|
||||
write_data(fp, "avih", 4); // FOURCC fcc; - 6
|
||||
write_long(fp, 56); // DWORD cb; - 7
|
||||
write_long(fp, 0); // DWORD dwMicroSecPerFrame; micros - updated on close - 8
|
||||
write_long(fp, 0); // DWORD dwMaxBytesPerSec; updated on close - 9
|
||||
write_long(fp, 4); // DWORD dwPaddingGranularity; - 10
|
||||
write_long(fp, 0); // DWORD dwFlags; - 11
|
||||
write_long(fp, 0); // DWORD dwTotalFrames; frames - updated on close - 12
|
||||
write_long(fp, 0); // DWORD dwInitialFrames; - 13
|
||||
write_long(fp, 1); // DWORD dwStreams; - 14
|
||||
write_long(fp, 0); // DWORD dwSuggestedBufferSize; - 15
|
||||
write_long(fp, width); // DWORD dwWidth; - 16
|
||||
write_long(fp, height); // DWORD dwHeight; - 17
|
||||
write_long(fp, 1000); // DWORD dwScale; - 18
|
||||
write_long(fp, 0); // DWORD dwRate; rate - updated on close - 19
|
||||
write_long(fp, 0); // DWORD dwStart; - 20
|
||||
write_long(fp, 0); // DWORD dwLength; length - updated on close - 21
|
||||
|
||||
write_data(fp, "LIST", 4); // FOURCC fcc; - 22
|
||||
write_long(fp, 116); // DWORD cb; - 23
|
||||
write_data(fp, "strl", 4); // FOURCC fcc; - 24
|
||||
|
||||
write_data(fp, "strh", 4); // FOURCC fcc; - 25
|
||||
write_long(fp, 56); // DWORD cb; - 26
|
||||
write_data(fp, "vids", 4); // FOURCC fccType; - 27
|
||||
write_data(fp, "MJPG", 4); // FOURCC fccHandler; - 28
|
||||
write_long(fp, 0); // DWORD dwFlags; - 29
|
||||
write_word(fp, 0); // WORD wPriority; - 30
|
||||
write_word(fp, 0); // WORD wLanguage; - 30.5
|
||||
write_long(fp, 0); // DWORD dwInitialFrames; - 31
|
||||
write_long(fp, 1000); // DWORD dwScale; - 32
|
||||
write_long(fp, 0); // DWORD dwRate; rate - updated on close - 33
|
||||
write_long(fp, 0); // DWORD dwStart; - 34
|
||||
write_long(fp, 0); // DWORD dwLength; length - updated on close - 35
|
||||
write_long(fp, 0); // DWORD dwSuggestedBufferSize; - 36
|
||||
write_long(fp, IM_JPEG_QUALITY * 100); // DWORD dwQuality; - 37
|
||||
write_long(fp, 0); // DWORD dwSampleSize; - 38
|
||||
write_word(fp, 0); // short int left; - 39
|
||||
write_word(fp, 0); // short int top; - 39.5
|
||||
write_word(fp, 0); // short int right; - 40
|
||||
write_word(fp, 0); // short int bottom; - 40.5
|
||||
|
||||
write_data(fp, "strf", 4); // FOURCC fcc; - 41
|
||||
write_long(fp, 40); // DWORD cb; - 42
|
||||
write_long(fp, 40); // DWORD biSize; - 43
|
||||
write_long(fp, width); // LONG biWidth; - 44
|
||||
write_long(fp, height); // LONG biHeight; - 45
|
||||
write_word(fp, 1); // WORD biPlanes; - 46
|
||||
write_word(fp, 24); // WORD biBitCount; - 46.5
|
||||
write_data(fp, "MJPG", 4); // DWORD biCompression; - 47
|
||||
write_long(fp, 0); // DWORD biSizeImage; - 48
|
||||
write_long(fp, 0); // LONG biXPelsPerMeter; - 49
|
||||
write_long(fp, 0); // LONG biYPelsPerMeter; - 50
|
||||
write_long(fp, 0); // DWORD biClrUsed; - 51
|
||||
write_long(fp, 0); // DWORD biClrImportant; - 52
|
||||
|
||||
write_data(fp, "LIST", 4); // FOURCC fcc; - 53
|
||||
write_long(fp, 0); // DWORD cb; movi - updated on close - 54
|
||||
write_data(fp, "movi", 4); // FOURCC fcc; - 55
|
||||
}
|
||||
|
||||
void mjpeg_add_frame(FIL *fp, uint32_t *frames, uint32_t *bytes, image_t *img)
|
||||
{
|
||||
write_data(fp, "00dc", 4); // FOURCC fcc;
|
||||
*frames += 1;
|
||||
if (IM_IS_JPEG(img)) {
|
||||
int pad = (((img->bpp + 3) / 4) * 4) - img->bpp;
|
||||
write_long(fp, img->bpp + pad); // DWORD cb;
|
||||
write_data(fp, img->pixels, img->bpp + pad); // reading past okay
|
||||
*bytes += img->bpp + pad;
|
||||
} else {
|
||||
uint32_t size;
|
||||
uint8_t *buffer = fb_alloc_all(&size);
|
||||
image_t out = { .w=img->w, .h=img->h, .bpp=size, .pixels=buffer };
|
||||
// When jpeg_compress needs more memory than in currently allocated it
|
||||
// will try to realloc. MP will detect that the pointer is outside of
|
||||
// the heap and return NULL which will cause an out of memory error.
|
||||
jpeg_compress(img, &out, IM_JPEG_QUALITY);
|
||||
int pad = (((out.bpp + 3) / 4) * 4) - out.bpp;
|
||||
write_long(fp, out.bpp + pad); // DWORD cb;
|
||||
write_data(fp, out.pixels, out.bpp + pad); // reading past okay
|
||||
*bytes += out.bpp + pad;
|
||||
fb_free();
|
||||
}
|
||||
}
|
||||
|
||||
void mjpeg_close(FIL *fp, uint32_t *frames, uint32_t *bytes, float fps)
|
||||
{
|
||||
// Needed
|
||||
file_seek(fp, SIZE_OFFSET);
|
||||
write_long(fp, 216 + (*frames * 8) + *bytes);
|
||||
// Needed
|
||||
file_seek(fp, MICROS_OFFSET);
|
||||
write_long(fp, (!fast_roundf(fps)) ? 0 :
|
||||
fast_roundf(1000000 / fps));
|
||||
write_long(fp, (!(*frames)) ? 0 :
|
||||
fast_roundf((((*frames * 8) + *bytes) * fps) / *frames));
|
||||
// Needed
|
||||
file_seek(fp, FRAMES_OFFSET);
|
||||
write_long(fp, *frames);
|
||||
// Probably not needed but writing it just in case.
|
||||
file_seek(fp, RATE_0_OFFSET);
|
||||
write_long(fp, fast_roundf(fps * 1000));
|
||||
// Probably not needed but writing it just in case.
|
||||
file_seek(fp, LENGTH_0_OFFSET);
|
||||
write_long(fp, (!fast_roundf(fps)) ? 0 :
|
||||
fast_roundf((*frames * 1000) / fps));
|
||||
// Probably not needed but writing it just in case.
|
||||
file_seek(fp, RATE_1_OFFSET);
|
||||
write_long(fp, fast_roundf(fps * 1000));
|
||||
// Probably not needed but writing it just in case.
|
||||
file_seek(fp, LENGTH_1_OFFSET);
|
||||
write_long(fp, (!fast_roundf(fps)) ? 0 :
|
||||
fast_roundf((*frames * 1000) / fps));
|
||||
// Needed
|
||||
file_seek(fp, MOVI_OFFSET);
|
||||
write_long(fp, 4 + (*frames * 8) + *bytes);
|
||||
file_close(fp);
|
||||
}
|
||||
119
src/omv/py/py_mjpeg.c
Normal file
119
src/omv/py/py_mjpeg.c
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* MJPEG Python module.
|
||||
*
|
||||
*/
|
||||
#include "mp.h"
|
||||
#include "ff_wrapper.h"
|
||||
#include "framebuffer.h"
|
||||
#include "sensor.h"
|
||||
#include "py_assert.h"
|
||||
#include "py_helper.h"
|
||||
#include "py_image.h"
|
||||
|
||||
static const mp_obj_type_t py_mjpeg_type; // forward declare
|
||||
// Mjpeg class
|
||||
typedef struct py_mjpeg_obj {
|
||||
mp_obj_base_t base;
|
||||
int width;
|
||||
int height;
|
||||
uint32_t frames;
|
||||
uint32_t bytes;
|
||||
FIL fp;
|
||||
} py_mjpeg_obj_t;
|
||||
|
||||
static mp_obj_t py_mjpeg_open(uint n_args, const mp_obj_t *args, mp_map_t *kw_args)
|
||||
{
|
||||
py_mjpeg_obj_t *mjpeg = m_new_obj(py_mjpeg_obj_t);
|
||||
mjpeg->width = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_width), fb->w);
|
||||
mjpeg->height = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_height), fb->h);
|
||||
mjpeg->frames = 0; // private
|
||||
mjpeg->bytes = 0; // private
|
||||
mjpeg->base.type = &py_mjpeg_type;
|
||||
|
||||
file_write_open(&mjpeg->fp, mp_obj_str_get_str(args[0]));
|
||||
mjpeg_open(&mjpeg->fp, mjpeg->width, mjpeg->height);
|
||||
return mjpeg;
|
||||
}
|
||||
|
||||
static mp_obj_t py_mjpeg_width(mp_obj_t mjpeg_obj)
|
||||
{
|
||||
py_mjpeg_obj_t *arg_mjpeg = mjpeg_obj;
|
||||
return mp_obj_new_int(arg_mjpeg->width);
|
||||
}
|
||||
|
||||
static mp_obj_t py_mjpeg_height(mp_obj_t mjpeg_obj)
|
||||
{
|
||||
py_mjpeg_obj_t *arg_mjpeg = mjpeg_obj;
|
||||
return mp_obj_new_int(arg_mjpeg->height);
|
||||
}
|
||||
|
||||
static mp_obj_t py_mjpeg_size(mp_obj_t mjpeg_obj)
|
||||
{
|
||||
py_mjpeg_obj_t *arg_mjpeg = mjpeg_obj;
|
||||
return mp_obj_new_int(f_size(&arg_mjpeg->fp));
|
||||
}
|
||||
|
||||
static mp_obj_t py_mjpeg_add_frame(mp_obj_t mjpeg_obj, mp_obj_t img_obj)
|
||||
{
|
||||
py_mjpeg_obj_t *arg_mjpeg = mjpeg_obj;
|
||||
image_t *arg_img = py_image_cobj(img_obj);
|
||||
PY_ASSERT_FALSE_MSG((arg_mjpeg->width != arg_img->w)
|
||||
|| (arg_mjpeg->height != arg_img->h),
|
||||
"Unexpected image geometry");
|
||||
|
||||
mjpeg_add_frame(&arg_mjpeg->fp, &arg_mjpeg->frames, &arg_mjpeg->bytes, arg_img);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
static mp_obj_t py_mjpeg_close(mp_obj_t mjpeg_obj, mp_obj_t fps_obj)
|
||||
{
|
||||
py_mjpeg_obj_t *arg_mjpeg = mjpeg_obj;
|
||||
mjpeg_close(&arg_mjpeg->fp, &arg_mjpeg->frames, &arg_mjpeg->bytes, mp_obj_get_float(fps_obj));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
static void py_mjpeg_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind)
|
||||
{
|
||||
py_mjpeg_obj_t *self = self_in;
|
||||
mp_printf(print, "<mjpeg width:%d height:%d>", self->width, self->height);
|
||||
}
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_mjpeg_width_obj, py_mjpeg_width);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_mjpeg_height_obj, py_mjpeg_height);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_mjpeg_size_obj, py_mjpeg_size);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_mjpeg_add_frame_obj, py_mjpeg_add_frame);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_mjpeg_close_obj, py_mjpeg_close);
|
||||
static const mp_map_elem_t locals_dict_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_width), (mp_obj_t)&py_mjpeg_width_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_height), (mp_obj_t)&py_mjpeg_height_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_size), (mp_obj_t)&py_mjpeg_size_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_add_frame), (mp_obj_t)&py_mjpeg_add_frame_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_close), (mp_obj_t)&py_mjpeg_close_obj },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(locals_dict, locals_dict_table);
|
||||
|
||||
static const mp_obj_type_t py_mjpeg_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_Mjpeg,
|
||||
.print = py_mjpeg_print,
|
||||
.locals_dict = (mp_obj_t)&locals_dict,
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_mjpeg_open_obj, 1, py_mjpeg_open);
|
||||
static const mp_map_elem_t globals_dict_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_mjpeg) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_Mjpeg), (mp_obj_t)&py_mjpeg_open_obj },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(globals_dict, globals_dict_table);
|
||||
|
||||
const mp_obj_module_t mjpeg_module = {
|
||||
.base = { &mp_type_module },
|
||||
.name = MP_QSTR_mjpeg,
|
||||
.globals = (mp_obj_t)&globals_dict,
|
||||
};
|
||||
31
usr/examples/mjpeg.py
Normal file
31
usr/examples/mjpeg.py
Normal file
@ -0,0 +1,31 @@
|
||||
# Mjpeg recording example:
|
||||
#
|
||||
# You can use your OpenMV Cam to record mjpeg files. You can either feed the
|
||||
# recorder object JPEG frames or RGB565/Grayscale frames. Once you've finished
|
||||
# recording an Mjpeg file you can use VLC to play it. If you're on Ubuntu then
|
||||
# the built-in video player will work too.
|
||||
|
||||
import sensor, image, time, mjpeg
|
||||
|
||||
sensor.reset()
|
||||
sensor.set_framesize(sensor.QVGA)
|
||||
sensor.set_pixformat(sensor.RGB565) # you can also use grayscale
|
||||
|
||||
# Warm up the cam
|
||||
for i in range(10):
|
||||
sensor.snapshot()
|
||||
|
||||
# FPS clock
|
||||
clock = time.clock()
|
||||
mjpeg = mjpeg.Mjpeg("/test.mjpeg") # video setup to use current resolution
|
||||
|
||||
for i in range(300):
|
||||
clock.tick()
|
||||
img = sensor.snapshot()
|
||||
mjpeg.add_frame(img)
|
||||
# Print FPS.
|
||||
# Note: Actual FPS is higher, the IDE slows down streaming.
|
||||
print(clock.fps())
|
||||
|
||||
mjpeg.close(clock.fps())
|
||||
print("done")
|
||||
Loading…
Reference in New Issue
Block a user