diff --git a/src/omv/img/gif.c b/src/omv/img/gif.c index e8c4b2d10..e9dee9278 100644 --- a/src/omv/img/gif.c +++ b/src/omv/img/gif.c @@ -6,13 +6,12 @@ * A super simple GIF encoder. * */ +#include "mp.h" #include "ff_wrapper.h" #include "imlib.h" #include "fb_alloc.h" -#define BLOCK_SIZE (126) // (2^(7)) - 2 -// TODO fix this, use all available FB memory -#define BUFF_SIZE (16*1024) // Multiple of (block size + 2) +#define BLOCK_SIZE (126) // (2^(7)) - 2 (DO NOT CHANGE!) void gif_open(FIL *fp, int width, int height, bool color, bool loop) { @@ -56,20 +55,36 @@ void gif_add_frame(FIL *fp, image_t *img, uint16_t delay) write_word(fp, img->h); write_data(fp, (uint8_t []) {0x00, 0x07}, 2); // 7-bits - int buf_ofs = 0; - int bytes = img->h * img->w; + uint32_t size; + uint8_t *buf = fb_alloc_all(&size); - // FatFS writes partial sectors (512-(fptr%512)) before writing the maximum contiguous sectors, - // increamenting the buffer during the process. This results in an unaligned buffer sent DMA. - // We intentionally misalign the buffer here so FatFS re-aligns it when writing the remainder. - int ff_ofs = f_tell(fp) % 4; - uint8_t *buf = fb_alloc((BUFF_SIZE+ff_ofs) * sizeof(*buf)); - buf += ff_ofs; + // This should never happen unless someone forgot to free. + if (size < (sizeof(uint32_t) * 2)) { // We need atleast 8 bytes. + nlr_raise(mp_obj_new_exception_msg(&mp_type_MemoryError, + "Memory leak detected!!!")); + } + + int buf_ofs = 0; + int bytes = img->h * img->w; + + // When a sector boundary is encountered while writing a file and there are + // more than 512 bytes left to write FatFs will detect that it can bypass + // its internal write buffer and pass the data buffer passed to it directly + // to the disk write function. However, the disk write function needs the + // buffer to be aligned to a 4-byte boundary. FatFs doesn't know this and + // will pass an unaligned buffer if we don't fix the issue. + + int fat_ofs = f_tell(fp) % 4; + buf += fat_ofs; + size -= fat_ofs; + + // Since we're about to do a more than 512 byte write which will trigger + // the above issue we can fix the problem by misaligning our buffer before + // calling write so that the first partial write gets us back aligned. for (int x=0; xfp)); } +static mp_obj_t py_gif_loop(mp_obj_t gif_obj) +{ + py_gif_obj_t *arg_gif = gif_obj; + return mp_obj_new_int(arg_gif->loop); +} + static mp_obj_t py_gif_add_frame(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { py_gif_obj_t *arg_gif = args[0]; @@ -96,6 +102,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_gif_width_obj, py_gif_width); STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_gif_height_obj, py_gif_height); STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_gif_format_obj, py_gif_format); STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_gif_size_obj, py_gif_size); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_gif_loop_obj, py_gif_loop); STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_gif_add_frame_obj, 2, py_gif_add_frame); STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_gif_close_obj, py_gif_close); static const mp_map_elem_t locals_dict_table[] = { @@ -103,6 +110,7 @@ static const mp_map_elem_t locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_height), (mp_obj_t)&py_gif_height_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_format), (mp_obj_t)&py_gif_format_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_size), (mp_obj_t)&py_gif_size_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_loop), (mp_obj_t)&py_gif_loop_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_add_frame), (mp_obj_t)&py_gif_add_frame_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_close), (mp_obj_t)&py_gif_close_obj }, { NULL, NULL }, diff --git a/usr/examples/gif.py b/usr/examples/gif.py index a61e8d5c4..1730f3ca1 100644 --- a/usr/examples/gif.py +++ b/usr/examples/gif.py @@ -1,27 +1,27 @@ -import time, sensor, image, gif +# Gif recording example: +# +# You can use your OpenMV Cam to record gif files. You can either feed the +# recorder object RGB565 frames or Grayscale frames. Use photo editing software +# like GIMP to compress and optimize the Gif before uploading it to the web. + +import sensor, image, time, gif -# Reset sensor sensor.reset() - -# Set sensor settings -sensor.set_contrast(1) -sensor.set_brightness(1) -sensor.set_saturation(1) -sensor.set_gainceiling(16) sensor.set_framesize(sensor.QQVGA) -sensor.set_pixformat(sensor.GRAYSCALE) +sensor.set_pixformat(sensor.RGB565) # you can also use grayscale -for i in range(30): - img = sensor.snapshot() +# Warm up the cam +for i in range(10): + sensor.snapshot() # FPS clock clock = time.clock() -gif = gif.Gif("/test.gif", loop=True) +gif = gif.Gif("/test.gif", loop=True) # video setup to use current resolution for i in range(30): clock.tick() img = sensor.snapshot() - gif.add_frame(img, delay=10) + gif.add_frame(img, delay=10) # centi seconds # Print FPS. # Note: Actual FPS is higher, the IDE slows down streaming. print(clock.fps()) diff --git a/usr/examples/mjpeg.py b/usr/examples/mjpeg.py index d3a75eab2..7d8848992 100644 --- a/usr/examples/mjpeg.py +++ b/usr/examples/mjpeg.py @@ -2,7 +2,7 @@ # # 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 +# recording a Mjpeg file you can use VLC to play it. If you are on Ubuntu then # the built-in video player will work too. import sensor, image, time, mjpeg @@ -19,7 +19,7 @@ for i in range(10): clock = time.clock() mjpeg = mjpeg.Mjpeg("/test.mjpeg") # video setup to use current resolution -for i in range(300): +for i in range(100): clock.tick() img = sensor.snapshot() mjpeg.add_frame(img)