This commit is contained in:
Devaev Maxim 2018-09-16 04:05:03 +03:00
parent c1d4356fd3
commit 65f61cdae9
6 changed files with 318 additions and 9 deletions

View File

@ -1,6 +1,6 @@
CC = gcc
CFLAGS = -c -Wall -Wextra
LDFLAGS =
LDFLAGS = -ljpeg
SOURCES = $(shell ls src/*.c)
OBJECTS = $(SOURCES:.c=.o)
PROG = ustreamer

View File

@ -5,6 +5,7 @@
#include "tools.h"
#include "device.h"
#include "jpeg.h"
static int _capture_init_loop(struct device *dev);
@ -87,7 +88,8 @@ void capture_loop(struct device *dev) {
}
LOG_DEBUG("Grabbed a new frame");
usleep(100000); // TODO: process dev->run->buffers[buf.index].start, buf.bytesused
jpeg_compress_buffer(dev, buf.index);
//usleep(100000); // TODO: process dev->run->buffers[buf.index].start, buf.bytesused
pass_frame:

View File

@ -39,6 +39,7 @@ static int _device_open_check_cap(struct device *dev);
static int _device_open_dv_timings(struct device *dev);
static int _device_apply_dv_timings(struct device *dev);
static int _device_open_format(struct device *dev);
static int _device_open_alloc_picbufs(struct device *dev);
static int _device_open_mmap(struct device *dev);
static int _device_open_queue_buffers(struct device *dev);
@ -109,6 +110,9 @@ int device_open(struct device *dev) {
if (_device_open_queue_buffers(dev) < 0) {
goto error;
}
if (_device_open_alloc_picbufs(dev) < 0) {
goto error;
}
LOG_DEBUG("Device fd=%d initialized", dev->run->fd);
return 0;
@ -119,12 +123,21 @@ int device_open(struct device *dev) {
}
void device_close(struct device *dev) {
if (dev->run->pictures) {
LOG_DEBUG("Releasing picture buffers ...");
for (unsigned index = 0; index < dev->run->n_buffers && dev->run->pictures[index]; ++index) {
free(dev->run->pictures[index]);
}
free(dev->run->pictures);
dev->run->pictures = NULL;
}
if (dev->run->buffers) {
LOG_DEBUG("Unmapping buffers ...");
LOG_DEBUG("Unmapping device buffers ...");
for (unsigned index = 0; index < dev->run->n_buffers; ++index) {
if (dev->run->buffers[index].start != MAP_FAILED) {
if (munmap(dev->run->buffers[index].start, dev->run->buffers[index].length) < 0) {
LOG_PERROR("Can't unmap buffer %d", index);
LOG_PERROR("Can't unmap device buffer %d", index);
}
}
}
@ -319,10 +332,11 @@ static int _device_open_mmap(struct device *dev) {
LOG_INFO("Requested %d device buffers, got %d", dev->n_buffers, req.count);
}
LOG_DEBUG("Allocating buffers ...");
LOG_DEBUG("Allocating device buffers ...");
dev->run->buffers = NULL;
if ((dev->run->buffers = calloc(req.count, sizeof(*dev->run->buffers))) == NULL) {
LOG_PERROR("Can't allocate buffers");
LOG_PERROR("Can't allocate device buffers");
return -1;
}
@ -334,17 +348,17 @@ static int _device_open_mmap(struct device *dev) {
buf.memory = V4L2_MEMORY_MMAP;
buf.index = dev->run->n_buffers;
LOG_DEBUG("Calling ioctl(VIDIOC_QUERYBUF) for buffer %d ...", dev->run->n_buffers);
LOG_DEBUG("Calling ioctl(VIDIOC_QUERYBUF) for device buffer %d ...", dev->run->n_buffers);
if (xioctl(dev->run->fd, VIDIOC_QUERYBUF, &buf) < 0) {
LOG_PERROR("Can't VIDIOC_QUERYBUF");
return -1;
}
LOG_DEBUG("Mapping buffer %d ...", dev->run->n_buffers);
LOG_DEBUG("Mapping device buffer %d ...", dev->run->n_buffers);
dev->run->buffers[dev->run->n_buffers].length = buf.length;
dev->run->buffers[dev->run->n_buffers].start = mmap(NULL, buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, dev->run->fd, buf.m.offset);
if (dev->run->buffers[dev->run->n_buffers].start == MAP_FAILED) {
LOG_PERROR("Can't map buffer %d", dev->run->n_buffers);
LOG_PERROR("Can't map device buffer %d", dev->run->n_buffers);
return -1;
}
}
@ -369,6 +383,26 @@ static int _device_open_queue_buffers(struct device *dev) {
return 0;
}
static int _device_open_alloc_picbufs(struct device *dev) {
LOG_DEBUG("Allocating picture buffers ...");
dev->run->pictures = NULL;
if ((dev->run->pictures = calloc(dev->run->n_buffers, sizeof(*dev->run->pictures))) == NULL) {
LOG_PERROR("Can't allocate picture buffers");
return -1;
}
dev->run->picture_size = dev->run->width * dev->run->height << 1;
for (unsigned index = 0; index < dev->run->n_buffers; ++index) {
LOG_DEBUG("Allocating picture buffer %d ...", index);
if ((dev->run->pictures[index] = malloc(dev->run->picture_size)) == NULL) {
LOG_PERROR("Can't allocate picture buffer %d", index);
return -1;
}
}
return 0;
}
static const char *_format_to_string_auto(char *buf, const size_t length, const unsigned format) {
if (length < 8) {
buf[0] = '\0';

View File

@ -21,6 +21,8 @@ struct device_runtime {
unsigned format;
unsigned n_buffers;
struct buffer *buffers;
unsigned picture_size;
unsigned char **pictures;
bool capturing;
};

263
src/jpeg.c Normal file
View File

@ -0,0 +1,263 @@
/*******************************************************************************
# Based on source code of mjpg-streamer. #
# #
# Orginally Copyright (C) 2005 2006 Laurent Pinchart && Michel Xhaard #
# Modifications Copyright (C) 2006 Gabriel A. Devenyi #
# Modifications Copyright (C) 2007 Tom Stöveken #
# Modifications Copyright (C) 2018 Maxim Devaev #
# #
# 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. #
# #
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #
# #
*******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jpeglib.h>
#include <linux/videodev2.h>
#include "tools.h"
#include "device.h"
#include "jpeg.h"
#define JPEG_OUTPUT_BUFFER_SIZE 4096
struct mjpg_destination_mgr {
struct jpeg_destination_mgr mgr; // Default manager
JOCTET *buffer; // Start of buffer
unsigned char *outbuffer_cursor;
int *written;
};
static void _jpeg_set_dest_picture(j_compress_ptr jpeg, unsigned char *picture, int *written);
static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg,
unsigned char *line_buffer, const unsigned char *data,
const unsigned width, const unsigned height);
static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg,
unsigned char *line_buffer, const unsigned char *data,
const unsigned width, const unsigned height);
static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg,
unsigned char *line_buffer, const unsigned char *data,
const unsigned width, const unsigned height);
static void _jpeg_init_destination(j_compress_ptr jpeg);
static boolean _jpeg_empty_output_buffer(j_compress_ptr jpeg);
static void _jpeg_term_destination(j_compress_ptr jpeg);
int jpeg_compress_buffer(struct device *dev, int index) {
// This function based on compress_image_to_jpeg() from mjpg-streamer
struct jpeg_compress_struct jpeg;
struct jpeg_error_mgr jpeg_error;
unsigned char *line_buffer;
int written = -1;
if ((line_buffer = calloc(dev->run->width * 3, sizeof(unsigned char))) == NULL) {
LOG_PERROR("Can't allocate memory for JPEG scanline");
return written;
}
jpeg.err = jpeg_std_error(&jpeg_error);
jpeg_create_compress(&jpeg);
_jpeg_set_dest_picture(&jpeg, dev->run->pictures[index], &written);
jpeg.image_width = dev->run->width;
jpeg.image_height = dev->run->height;
jpeg.input_components = 3;
jpeg.in_color_space = JCS_RGB;
jpeg_set_defaults(&jpeg);
jpeg_set_quality(&jpeg, dev->jpeg_quality, TRUE);
jpeg_start_compress(&jpeg, TRUE);
# define WRITE_SCANLINES(_func) \
_func(&jpeg, line_buffer, dev->run->buffers[index].start, dev->run->width, dev->run->height)
switch (dev->run->format) {
case V4L2_PIX_FMT_YUYV: WRITE_SCANLINES(_jpeg_write_scanlines_yuyv); break;
case V4L2_PIX_FMT_UYVY: WRITE_SCANLINES(_jpeg_write_scanlines_uyvy); break;
case V4L2_PIX_FMT_RGB565: WRITE_SCANLINES(_jpeg_write_scanlines_rgb565); break;
default:
LOG_ERROR("Unsupported input format for JPEG compressor");
written = -1;
break;
}
// TODO: process jpeg errors:
// https://stackoverflow.com/questions/19857766/error-handling-in-libjpeg
jpeg_finish_compress(&jpeg);
jpeg_destroy_compress(&jpeg);
free(line_buffer);
return written;
}
static void _jpeg_set_dest_picture(j_compress_ptr jpeg, unsigned char *picture, int *written) {
struct mjpg_destination_mgr *dest;
if (jpeg->dest == NULL) {
jpeg->dest = (struct jpeg_destination_mgr *)(*jpeg->mem->alloc_small)(
(j_common_ptr) jpeg, JPOOL_PERMANENT, sizeof(struct mjpg_destination_mgr)
);
}
dest = (struct mjpg_destination_mgr *) jpeg->dest;
dest->mgr.init_destination = _jpeg_init_destination;
dest->mgr.empty_output_buffer = _jpeg_empty_output_buffer;
dest->mgr.term_destination = _jpeg_term_destination;
dest->outbuffer_cursor = picture;
dest->written = written;
}
static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg,
unsigned char *line_buffer, const unsigned char *data,
const unsigned width, const unsigned height) {
JSAMPROW scanlines[1];
unsigned z = 0;
while (jpeg->next_scanline < height) {
unsigned char *ptr = line_buffer;
for (unsigned x = 0; x < width; x++) {
int y = (!z ? data[0] << 8 : data[2] << 8);
int u = data[1] - 128;
int v = data[3] - 128;
int r = (y + (359 * v)) >> 8;
int g = (y - (88 * u) - (183 * v)) >> 8;
int b = (y + (454 * u)) >> 8;
*(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r);
*(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g);
*(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b);
if (z++) {
z = 0;
data += 4;
}
}
scanlines[0] = line_buffer;
jpeg_write_scanlines(jpeg, scanlines, 1);
}
}
static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg,
unsigned char *line_buffer, const unsigned char *data,
const unsigned width, const unsigned height) {
JSAMPROW scanlines[1];
unsigned z = 0;
while(jpeg->next_scanline < height) {
unsigned char *ptr = line_buffer;
for(unsigned x = 0; x < width; x++) {
int y = (!z ? data[1] << 8 : data[3] << 8);
int u = data[0] - 128;
int v = data[2] - 128;
int r = (y + (359 * v)) >> 8;
int g = (y - (88 * u) - (183 * v)) >> 8;
int b = (y + (454 * u)) >> 8;
*(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r);
*(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g);
*(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b);
if (z++) {
z = 0;
data += 4;
}
}
scanlines[0] = line_buffer;
jpeg_write_scanlines(jpeg, scanlines, 1);
}
}
static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg,
unsigned char *line_buffer, const unsigned char *data,
const unsigned width, const unsigned height) {
JSAMPROW scanlines[1];
while(jpeg->next_scanline < height) {
unsigned char *ptr = line_buffer;
for(unsigned x = 0; x < width; x++) {
unsigned int two_byte = (data[1] << 8) + data[0];
*(ptr++) = data[1] & 248;
*(ptr++) = (unsigned char)((two_byte & 2016) >> 3);
*(ptr++) = (data[0] & 31) * 8;
data += 2;
}
scanlines[0] = line_buffer;
jpeg_write_scanlines(jpeg, scanlines, 1);
}
}
static void _jpeg_init_destination(j_compress_ptr jpeg) {
struct mjpg_destination_mgr *dest = (struct mjpg_destination_mgr *) jpeg->dest;
// Allocate the output buffer --- it will be released when done with image
dest->buffer = (JOCTET *)(*jpeg->mem->alloc_small)(
(j_common_ptr) jpeg, JPOOL_IMAGE, JPEG_OUTPUT_BUFFER_SIZE * sizeof(JOCTET)
);
dest->mgr.next_output_byte = dest->buffer;
dest->mgr.free_in_buffer = JPEG_OUTPUT_BUFFER_SIZE;
}
static boolean _jpeg_empty_output_buffer(j_compress_ptr jpeg) {
// Called whenever local jpeg buffer fills up
struct mjpg_destination_mgr *dest = (struct mjpg_destination_mgr *) jpeg->dest;
memcpy(dest->outbuffer_cursor, dest->buffer, JPEG_OUTPUT_BUFFER_SIZE);
dest->outbuffer_cursor += JPEG_OUTPUT_BUFFER_SIZE;
*dest->written += JPEG_OUTPUT_BUFFER_SIZE;
dest->mgr.next_output_byte = dest->buffer;
dest->mgr.free_in_buffer = JPEG_OUTPUT_BUFFER_SIZE;
return TRUE;
}
static void _jpeg_term_destination(j_compress_ptr jpeg) {
// Called by jpeg_finish_compress after all data has been written.
// Usually needs to flush buffer
struct mjpg_destination_mgr *dest = (struct mjpg_destination_mgr *) jpeg->dest;
size_t data_count = JPEG_OUTPUT_BUFFER_SIZE - dest->mgr.free_in_buffer;
// Write any data remaining in the buffer
memcpy(dest->outbuffer_cursor, dest->buffer, data_count);
dest->outbuffer_cursor += data_count;
*dest->written += data_count;
}

8
src/jpeg.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <stdlib.h>
#include <stddef.h>
#include "device.h"
int jpeg_compress_buffer(struct device *dev, int index);