/** * Compresses video frame with tiles API * * @param[in] s compress state * @param[in] frame uncompressed frame * @param buffer_index 0 or 1 - driver should have 2 output buffers, filling the selected one. * Returned video frame should stay valid until requesting compress with the * same index. * @param parent parent module (for the case when there is a need to reconfigure) * @return compressed video frame, may be NULL if compression failed */ static struct video_frame *compress_frame_tiles(struct compress_state_real *s, struct video_frame *frame, int buffer_index, struct module *parent) { if(frame->tile_count != s->state_count) { s->state = realloc(s->state, frame->tile_count * sizeof(struct module *)); for(unsigned int i = s->state_count; i < frame->tile_count; ++i) { char compress_options[1024]; strncpy(compress_options, s->compress_options, sizeof(compress_options)); compress_options[sizeof(compress_options) - 1] = '\0'; s->state[i] = s->handle->init_func(parent, compress_options); if(!s->state[i]) { fprintf(stderr, "Compression initialization failed\n"); return NULL; } } for(int i = 0; i < 2; ++i) { vf_free(s->out_frame[i]); s->out_frame[i] = vf_alloc(frame->tile_count); } s->state_count = frame->tile_count; } task_result_handle_t task_handle[frame->tile_count]; struct compress_worker_data data_tile[frame->tile_count]; for(unsigned int i = 0; i < frame->tile_count; ++i) { struct compress_worker_data *data = &data_tile[i]; data->state = s->state[i]; data->tile = &frame->tiles[i]; data->desc = video_desc_from_frame(frame); data->desc.tile_count = 1; data->buffer_index = buffer_index;; data->callback = s->handle->compress_tile_func; task_handle[i] = task_run_async(compress_tile_callback, data); } for(unsigned int i = 0; i < frame->tile_count; ++i) { struct compress_worker_data *data = wait_task(task_handle[i]); if(i == 0) { // update metadata from first tile data->desc.tile_count = frame->tile_count; vf_write_desc(s->out_frame[buffer_index], data->desc); } if(data->ret) { memcpy(&s->out_frame[buffer_index]->tiles[i], data->ret, sizeof(struct tile)); } else { return NULL; } } return s->out_frame[buffer_index]; }
static struct video_frame *filter(void *, struct video_frame *in) { if (in->color_spec != UYVY) { log_msg(LOG_LEVEL_WARNING, "Only supporte colorspace for mirror is currently UYVY!\n"); return in; } struct video_frame *out = vf_alloc_desc_data(video_desc_from_frame(in)); out->dispose = vf_free; unsigned char *in_data = (unsigned char *) in->tiles[0].data; unsigned char *out_data = (unsigned char *) out->tiles[0].data; int linesize = vc_get_linesize(in->tiles[0].width, in->color_spec); for (unsigned int y = 0; y < in->tiles[0].height; ++y) { mirror_line_UYVY(out_data + y * linesize, in_data + y * linesize, linesize); } VIDEO_FRAME_DISPOSE(in); return out; }
void video_export(struct video_export *s, struct video_frame *frame) { if(!s) { return; } assert(frame != NULL); if(s->saved_desc.width == 0) { s->saved_desc = video_desc_from_frame(frame); } else { if(!video_desc_eq(s->saved_desc, video_desc_from_frame(frame))) { fprintf(stderr, "[Video export] Format change detected, not exporting.\n"); return; } } for (unsigned int i = 0; i < frame->tile_count; ++i) { assert(frame->tiles[i].data != NULL && frame->tiles[i].data_len != 0); struct output_entry *entry = malloc(sizeof(struct output_entry)); entry->data_len = frame->tiles[i].data_len; entry->data = (char *) malloc(entry->data_len); entry->filename = malloc(512); entry->next = NULL; if(frame->tile_count == 1) { snprintf(entry->filename, 512, "%s/%08d.%s", s->path, s->total + 1, get_codec_file_extension(frame->color_spec)); } else { // add also tile index snprintf(entry->filename, 512, "%s/%08d_%d.%s", s->path, s->total + 1, i, get_codec_file_extension(frame->color_spec)); } memcpy(entry->data, frame->tiles[i].data, entry->data_len); pthread_mutex_lock(&s->lock); { // check if we do not occupy too much memory if(s->queue_len >= MAX_QUEUE_SIZE) { fprintf(stderr, "[Video export] Maximal queue size (%d) exceeded, not saving frame %d.\n", MAX_QUEUE_SIZE, s->total++); // we increment total size to keep the index pthread_mutex_unlock(&s->lock); free(entry->data); free(entry); return; } if(s->head) { s->tail->next = entry; s->tail = entry; } else { s->head = s->tail = entry; } s->queue_len += 1; } pthread_mutex_unlock(&s->lock); platform_sem_post(&s->semaphore); } s->total += 1; }
struct video_frame * jpeg_compress(struct module *mod, struct video_frame * tx, int buffer_idx) { struct state_video_compress_jpeg *s = (struct state_video_compress_jpeg *) mod->priv_data; int i; unsigned char *line1, *line2; struct video_frame *out; unsigned int x; cudaSetDevice(cuda_devices[0]); if(!s->encoder) { int ret; ret = configure_with(s, tx); if(!ret) { return NULL; } } struct video_desc desc; desc = video_desc_from_frame(tx); // if format changed, reconfigure if(!video_desc_eq_excl_param(s->saved_desc, desc, PARAM_INTERLACING)) { cleanup_state(s); int ret; ret = configure_with(s, tx); if(!ret) { return NULL; } } out = s->out[buffer_idx]; for (x = 0; x < tx->tile_count; ++x) { struct tile *in_tile = vf_get_tile(tx, x); struct tile *out_tile = vf_get_tile(out, x); line1 = (unsigned char *) in_tile->data; line2 = (unsigned char *) s->decoded; for (i = 0; i < (int) in_tile->height; ++i) { s->decoder(line2, line1, s->encoder_input_linesize, 0, 8, 16); line1 += vc_get_linesize(in_tile->width, tx->color_spec); line2 += s->encoder_input_linesize; } line1 = (unsigned char *) out_tile->data + (in_tile->height - 1) * s->encoder_input_linesize; for( ; i < (int) out->tiles[0].height; ++i) { memcpy(line2, line1, s->encoder_input_linesize); line2 += s->encoder_input_linesize; } /*if(s->interlaced_input) vc_deinterlace((unsigned char *) s->decoded, s->encoder_input_linesize, s->out->tiles[0].height);*/ uint8_t *compressed; int size; int ret; struct gpujpeg_encoder_input encoder_input; gpujpeg_encoder_input_set_image(&encoder_input, (uint8_t *) s->decoded); ret = gpujpeg_encoder_encode(s->encoder, &encoder_input, &compressed, &size); if(ret != 0) return NULL; out_tile->data_len = size; memcpy(out_tile->data, compressed, size); } return out; }