23 #include <fvutils/compression/jpeg_compressor.h> 24 #include <fvutils/compression/jpeg_compressor_mmal.h> 26 #include <core/exception.h> 27 #include <core/threading/mutex.h> 28 #include <core/threading/wait_condition.h> 33 #include <mmal/mmal.h> 34 #include <mmal/mmal_logging.h> 35 #include <mmal/mmal_buffer.h> 36 #include <mmal/util/mmal_util.h> 37 #include <mmal/util/mmal_util_params.h> 38 #include <mmal/util/mmal_default_components.h> 39 #include <mmal/util/mmal_connection.h> 54 class JpegImageCompressorMMAL::State {
57 frame_complete_ =
false;
61 compdest = ImageCompressor::COMP_DEST_MEM;
64 encoder_component = NULL;
65 encoder_pool_in = NULL;
66 encoder_pool_out = NULL;
75 CompressionDestination compdest;
80 unsigned int jpeg_buffer_size;
81 unsigned int jpeg_bytes;
83 MMAL_COMPONENT_T *encoder_component;
85 MMAL_POOL_T *encoder_pool_in;
86 MMAL_POOL_T *encoder_pool_out;
102 encoder_output_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
104 bool complete =
false;
107 JpegImageCompressorMMAL::State *state =
108 (JpegImageCompressorMMAL::State *)port->userdata;
111 size_t bytes_written = buffer->length;
113 if (buffer->length) {
114 mmal_buffer_header_mem_lock(buffer);
115 if (state->compdest == ImageCompressor::COMP_DEST_FILE && state->file_handle) {
116 bytes_written = fwrite(buffer->data, 1, buffer->length, state->file_handle);
117 }
else if (state->compdest == ImageCompressor::COMP_DEST_MEM && state->buffer) {
118 if (state->jpeg_bytes + bytes_written <= state->jpeg_buffer_size) {
119 memcpy(state->buffer, buffer->data, buffer->length);
120 state->buffer += buffer->length;
121 state->jpeg_bytes += buffer->length;
123 printf(
"Buffer overflow: %zu + %zu > %zu\n", state->jpeg_bytes, bytes_written, state->jpeg_buffer_size);
126 mmal_buffer_header_mem_unlock(buffer);
130 if (bytes_written != buffer->length) {
131 printf(
"Unable to write buffer to file - aborting");
137 if (buffer->flags & (MMAL_BUFFER_HEADER_FLAG_FRAME_END | MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED)) {
143 printf(
"Received a encoder buffer callback with no state");
147 mmal_buffer_header_release(buffer);
150 if (port->is_enabled) {
151 MMAL_STATUS_T status = MMAL_SUCCESS;
152 MMAL_BUFFER_HEADER_T *new_buffer;
154 new_buffer = mmal_queue_get(state->encoder_pool_out->queue);
157 status = mmal_port_send_buffer(port, new_buffer);
159 if (!new_buffer || status != MMAL_SUCCESS)
160 printf(
"Unable to return a buffer to the encoder port");
164 state->frame_complete_mutex_->lock();
165 state->frame_complete_ =
true;
166 state->frame_complete_waitcond_->wake_all();
167 state->frame_complete_mutex_->unlock();
171 static void encoder_input_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
174 mmal_buffer_header_release(buffer);
191 JpegImageCompressorMMAL::JpegImageCompressorMMAL(
unsigned int quality)
194 width_ = height_ = 0;
196 state_ =
new State();
202 JpegImageCompressorMMAL::~JpegImageCompressorMMAL()
204 destroy_encoder_component();
210 JpegImageCompressorMMAL::supports_vflip()
217 JpegImageCompressorMMAL::set_vflip(
bool enable)
223 JpegImageCompressorMMAL::compress()
227 MMAL_PORT_T *encoder_input = NULL;
228 MMAL_PORT_T *encoder_output = NULL;
230 MMAL_STATUS_T status = MMAL_SUCCESS;
233 if (mmal_component_enable(state_->encoder_component) != MMAL_SUCCESS) {
234 mmal_component_destroy(state_->encoder_component);
235 throw Exception(
"Unable to enable video encoder component");
238 encoder_input = state_->encoder_component->input[0];
239 encoder_output = state_->encoder_component->output[0];
241 if (state_->compdest == ImageCompressor::COMP_DEST_FILE) {
242 state_->file_handle = fopen(filename_,
"wb");
243 if (! state_->file_handle) {
244 throw Exception(errno,
"Failed to open output file");
248 state_->frame_complete_mutex_->lock();
249 state_->frame_complete_ =
false;
250 state_->frame_complete_mutex_->unlock();
252 encoder_output->userdata = (::MMAL_PORT_USERDATA_T *)state_;
255 status = mmal_port_enable(encoder_output, encoder_output_buffer_callback);
258 int num = mmal_queue_length(state_->encoder_pool_out->queue);
260 for (
int q = 0; q < num; ++q) {
261 MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state_->encoder_pool_out->queue);
264 printf(
"Unable to get a required buffer %d from pool queue", q);
266 if (mmal_port_send_buffer(encoder_output, buffer)!= MMAL_SUCCESS)
267 printf(
"Unable to send a buffer to encoder output port (%d)", q);
271 status = mmal_port_enable(encoder_input, encoder_input_buffer_callback);
273 MMAL_BUFFER_HEADER_T *buffer;
274 if ((buffer = mmal_queue_get(state_->encoder_pool_in->queue)) != NULL)
276 size_t exp_size = colorspace_buffer_size(YUV422_PLANAR,
277 encoder_input->format->es->video.width,
278 encoder_input->format->es->video.height);
279 if (buffer->alloc_size < exp_size) {
280 printf(
"Too small buffer");
286 char *data = (
char *)buffer->data;
287 char *imgb = (
char *)buffer_;
291 for (h = 0; h < encoder_input->format->es->video.height; ++h) {
292 memcpy(data, imgb + ((height_ - h - 1) * width_), width_);
294 data += encoder_input->format->es->video.width;
297 for (h = 0; h < encoder_input->format->es->video.height; ++h) {
298 memcpy(data, imgb + (width_ * height_) + ((height_ - h - 1) * (width_/2)), width_ / 2);
300 data += encoder_input->format->es->video.width / 2;
303 for (h = 0; h < encoder_input->format->es->video.height; ++h) {
304 memcpy(data, imgb + (width_ * height_) + ((width_/2) * height_) + ((height_ - h - 1) * (width_/2)), width_ / 2);
307 data += encoder_input->format->es->video.width / 2;
310 for (h = 0; h < encoder_input->format->es->video.height; ++h) {
311 memcpy(data, imgb, width_);
313 data += encoder_input->format->es->video.width;
316 for (h = 0; h < encoder_input->format->es->video.height * 2; ++h) {
317 memcpy(data, imgb, width_ / 2);
319 data += encoder_input->format->es->video.width / 2;
323 buffer->length = (size_t)(data - (
char *)buffer->data);
324 buffer->flags = MMAL_BUFFER_HEADER_FLAG_EOS;
326 status = mmal_port_send_buffer(encoder_input, buffer);
327 if (status != MMAL_SUCCESS) {
328 printf(
"Unable to send input buffer: %x\n", status);
331 state_->frame_complete_mutex_->lock();
332 while (! state_->frame_complete_) {
333 state_->frame_complete_waitcond_->wait();
335 state_->frame_complete_mutex_->unlock();
338 if (encoder_input && encoder_input->is_enabled)
339 mmal_port_disable(encoder_input);
340 if (encoder_output && encoder_output->is_enabled)
341 mmal_port_disable(encoder_output);
343 if (state_->compdest == ImageCompressor::COMP_DEST_FILE) {
344 fclose(state_->file_handle);
345 state_->file_handle = NULL;
351 JpegImageCompressorMMAL::set_image_dimensions(
unsigned int width,
unsigned int height)
353 if (width_ != width || height_ != height) {
356 destroy_encoder_component();
357 create_encoder_component();
363 JpegImageCompressorMMAL::set_image_buffer(colorspace_t cspace,
unsigned char *buffer)
365 if ( cspace == YUV422_PLANAR ) {
368 throw Exception(
"JpegImageCompressorMMAL: can only accept YUV422_PLANAR buffers");
376 state_->compdest = cd;
388 JpegImageCompressorMMAL::set_destination_buffer(
unsigned char *buf,
unsigned int buf_size)
390 state_->jpeg_buffer = (
char *)buf;
391 state_->jpeg_buffer_size = buf_size;
396 JpegImageCompressorMMAL::compressed_size()
398 return state_->jpeg_bytes;
402 JpegImageCompressorMMAL::recommended_compressed_buffer_size()
404 return width_ * height_ * 2;
409 JpegImageCompressorMMAL::set_filename(
const char *filename)
411 filename_ = filename;
416 JpegImageCompressorMMAL::create_encoder_component()
418 MMAL_COMPONENT_T *encoder = 0;
419 MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL;
420 MMAL_STATUS_T status;
423 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER, &encoder);
425 if (status != MMAL_SUCCESS) {
427 mmal_component_destroy(encoder);
428 throw Exception(
"Unable to create JPEG encoder component");
431 if (!encoder->input_num || !encoder->output_num) {
432 mmal_component_destroy(encoder);
433 throw Exception(
"JPEG encoder doesn't have input/output ports");
436 encoder_input = encoder->input[0];
437 encoder_output = encoder->output[0];
439 memset(&encoder_input->format->es->video, 0,
sizeof(MMAL_VIDEO_FORMAT_T));
440 encoder_input->format->es->video.width = width_;
441 encoder_input->format->es->video.height = height_;
442 encoder_input->format->es->video.crop.x = 0;
443 encoder_input->format->es->video.crop.y = 0;
444 encoder_input->format->es->video.crop.width = width_;
445 encoder_input->format->es->video.crop.height = height_;
446 encoder_input->format->es->video.frame_rate.num = 1;
447 encoder_input->format->es->video.frame_rate.den = 1;
450 mmal_format_copy(encoder_output->format, encoder_input->format);
453 encoder_input->format->flags = 0;
454 encoder_input->format->encoding = MMAL_ENCODING_I422;
457 encoder_output->format->encoding = MMAL_ENCODING_JPEG;
459 encoder_output->buffer_size = encoder_output->buffer_size_recommended * 2;
460 if (encoder_output->buffer_size < encoder_output->buffer_size_min)
461 encoder_output->buffer_size = encoder_output->buffer_size_min;
464 status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_Q_FACTOR, quality_);
465 if (status != MMAL_SUCCESS) {
466 mmal_component_destroy(encoder);
467 throw Exception(
"Unable to set JPEG quality");
470 status = mmal_port_parameter_set_boolean(encoder_output,
471 MMAL_PARAMETER_EXIF_DISABLE, 1);
472 if (status != MMAL_SUCCESS) {
473 mmal_component_destroy(encoder);
474 throw Exception(
"Unable to disable JPEG EXIF data");
478 status = mmal_port_format_commit(encoder_output);
480 if (status != MMAL_SUCCESS) {
481 mmal_component_destroy(encoder);
482 throw Exception(
"Unable to set format on video encoder output port");
486 status = mmal_port_format_commit(encoder_input);
488 if (status != MMAL_SUCCESS) {
489 mmal_component_destroy(encoder);
490 throw Exception(
"Unable to set format on input encoder port");
494 MMAL_PARAMETER_THUMBNAIL_CONFIG_T param_thumb =
495 {{MMAL_PARAMETER_THUMBNAIL_CONFIGURATION,
496 sizeof(MMAL_PARAMETER_THUMBNAIL_CONFIG_T)}, 0, 0, 0, 0};
497 status = mmal_port_parameter_set(encoder->control, ¶m_thumb.hdr);
501 pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size);
504 mmal_component_destroy(encoder);
505 throw Exception(
"Failed to create buffer header pool for encoder output port %s", encoder_output->name);
508 state_->encoder_pool_out = pool;
511 pool = mmal_port_pool_create(encoder_input, encoder_input->buffer_num, encoder_input->buffer_size);
514 mmal_component_destroy(encoder);
515 throw Exception(
"Failed to create buffer header pool for encoder input port %s", encoder_input->name);
518 state_->encoder_pool_in = pool;
519 state_->encoder_component = encoder;
524 JpegImageCompressorMMAL::destroy_encoder_component()
526 mmal_component_disable(state_->encoder_component);
528 if (state_->encoder_pool_in) {
529 mmal_port_pool_destroy(state_->encoder_component->input[0], state_->encoder_pool_in);
530 state_->encoder_pool_in = NULL;
533 if (state_->encoder_pool_out) {
534 mmal_port_pool_destroy(state_->encoder_component->output[0], state_->encoder_pool_out);
535 state_->encoder_pool_out = NULL;
538 if (state_->encoder_component) {
539 mmal_component_destroy(state_->encoder_component);
540 state_->encoder_component = NULL;
Wait until a given condition holds.
Fawkes library namespace.
CompressionDestination
Where to put the compressed image.
Base class for exceptions in Fawkes.
Mutex mutual exclusion lock.