UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
video_export.c
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2012-2024 CESNET, z. s. p. o.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#include <assert.h> // for assert
40#include <pthread.h>
41#include <stdint.h> // for uint32_t
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h> // for memcpy, memset, strdup
45
46#include "types.h" // for tile, video_frame, video_desc
47#include "utils/fs.h" // for MAX_PATH_SIZE
48#include "video_codec.h"
49#include "video_export.h"
50#include "video_frame.h" // for video_desc_from_frame, video_...
51
52#define MAX_QUEUE_SIZE 300
53
54/*
55 * we do not need to have possible stalls, so IO is performend in a separate thread
56 */
57static void *video_export_thread(void *arg);
58void output_summary(struct video_export *s);
59
61 char *filename;
62 char *data;
64
66};
67
69 char *path;
70
71 uint32_t total;
72
73 pthread_mutex_t lock;
74 struct output_entry * volatile head,
75 * volatile tail;
76 volatile int queue_len;
77 sem_t semaphore;
78
80
81 pthread_t thread_id;
82};
83
84static void *video_export_thread(void *arg)
85{
86 struct video_export *s = (struct video_export *) arg;
87
88 while(1) {
90 struct output_entry *current;
91
92 pthread_mutex_lock(&s->lock);
93 {
94 current = s->head;
95 s->head = s->head->next;
96 s->queue_len -= 1;
97 }
98 pthread_mutex_unlock(&s->lock);
99
100 assert((current->data == NULL && current->data_len == 0) ||
101 (current->data != NULL && current->data_len != 0));
102
103 // poison
104 if(current->data == NULL) {
105 return NULL;
106 }
107
108 FILE *out = fopen(current->filename, "wb");
109 if (out == NULL) {
110 perror("fopen");
111 } else {
112 if (fwrite(current->data, current->data_len, 1, out) != 1) {
113 perror("fwrite");
114 }
115 fclose(out);
116 }
117 free(current->data);
118 free(current->filename);
119 free(current);
120 }
121
122 // never get here
123}
124
126{
127 struct video_export *s = calloc(1, sizeof *s);
128 assert(s != NULL);
129
130 platform_sem_init(&s->semaphore, 0, 0);
131 pthread_mutex_init(&s->lock, NULL);
132 s->total = s->queue_len = 0;
133 assert(path != NULL);
134 s->path = strdup(path);
135 s->head = s->tail = NULL;
136
137 memset(&s->saved_desc, 0, sizeof(s->saved_desc));
138
139 if(pthread_create(&s->thread_id, NULL, video_export_thread, s) != 0) {
140 fprintf(stderr, "[Video exporter] Failed to create thread.\n");
141 free(s);
142 return NULL;
143 }
144
145 return s;
146}
147
149{
150 char name[MAX_PATH_SIZE];
151 snprintf(name, sizeof name, "%s/video.info", s->path);
152
153 FILE *summary = fopen(name, "w");
154
155 if(!summary) {
156 perror("Cannot write video export summary file");
157 return;
158 }
159
160 fprintf(summary, "version %d\n", VIDEO_EXPORT_SUMMARY_VERSION);
161 fprintf(summary, "width %d\n", s->saved_desc.width);
162 fprintf(summary, "height %d\n", s->saved_desc.height);
163 uint32_t fourcc = get_fourcc(s->saved_desc.color_spec);
164 fprintf(summary, "fourcc %.4s\n", (char *) &fourcc);
165 fprintf(summary, "fps %.2f\n", s->saved_desc.fps);
166 fprintf(summary, "interlacing %d\n", (int) s->saved_desc.interlacing);
167 fprintf(summary, "count %d\n", s->total);
168
169 fclose(summary);
170}
171
173{
174 if(s) {
175 // poison
176 struct output_entry *entry = calloc(1, sizeof(struct output_entry));
177
178 pthread_mutex_lock(&s->lock);
179 {
180 if(s->head) {
181 s->tail->next = entry;
182 s->tail = entry;
183 } else {
184 s->head = s->tail = entry;
185 }
186 }
187 pthread_mutex_unlock(&s->lock);
189
190 pthread_join(s->thread_id, NULL);
191 pthread_mutex_destroy(&s->lock);
192
193 // write summary
194 if(s->total > 0) {
196 }
197
198 free(s->path);
199 free(s);
200 }
201}
202
204{
205 if(!s) {
206 return;
207 }
208
209 assert(frame != NULL);
210 s->total += 1;
211
212 if(s->saved_desc.width == 0) {
214 } else {
216 fprintf(stderr, "[Video export] Format change detected, not exporting.\n");
217 return;
218 }
219 }
220
221 for (unsigned int i = 0; i < frame->tile_count; ++i) {
222 assert(frame->tiles[i].data != NULL && frame->tiles[i].data_len != 0);
223
224 struct output_entry *entry = malloc(sizeof(struct output_entry));
225
226 entry->data_len = frame->tiles[i].data_len;
227 entry->data = (char *) malloc(entry->data_len);
228 entry->filename = malloc(MAX_PATH_SIZE);
229 entry->next = NULL;
230
231 if(frame->tile_count == 1) {
232 snprintf(entry->filename, MAX_PATH_SIZE, "%s/%08d.%s", s->path,
233 s->total,
235 } else {
236 // add also tile index
237 snprintf(entry->filename, MAX_PATH_SIZE, "%s/%08d_%d.%s", s->path,
238 s->total, i,
240 }
241 memcpy(entry->data, frame->tiles[i].data, entry->data_len);
242
243 pthread_mutex_lock(&s->lock);
244 {
245 // check if we do not occupy too much memory
246 if(s->queue_len >= MAX_QUEUE_SIZE) {
247 fprintf(stderr, "[Video export] Maximal queue size (%d) exceeded, not saving frame %d.\n",
249 s->total); // we increment total size to keep the index
250 pthread_mutex_unlock(&s->lock);
251 free(entry->data);
252 free(entry);
253 return;
254 }
255
256 if(s->head) {
257 s->tail->next = entry;
258 s->tail = entry;
259 } else {
260 s->head = s->tail = entry;
261 }
262 s->queue_len += 1;
263 }
264 pthread_mutex_unlock(&s->lock);
265
267 }
268}
269
struct video_frame * frame
struct video_desc video_desc_from_frame(const struct video_frame *frame)
Returns struct video_desc from video frame.
#define MAX_PATH_SIZE
Definition fs.h:48
void platform_sem_post(void *semStructure)
void platform_sem_wait(void *semStructure)
void platform_sem_init(void *semStructure, int pshared, int initialValue)
const char * name
Definition settings.cpp:193
char * data
struct output_entry * next
char * filename
int data_len
char * data
Raw tile data. Pointer must be at least 4B aligned.
Definition types.h:287
unsigned int data_len
length of the data
Definition types.h:288
codec_t color_spec
Definition types.h:182
enum interlacing_t interlacing
Definition types.h:184
double fps
Definition types.h:183
unsigned int width
width of every tile (!)
Definition types.h:179
unsigned int height
height of every tile (!)
Definition types.h:180
struct output_entry *volatile *volatile tail
uint32_t total
struct output_entry *volatile head
pthread_t thread_id
pthread_mutex_t lock
volatile int queue_len
struct video_desc saved_desc
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
Definition types.h:345
unsigned int tile_count
Definition types.h:344
codec_t color_spec
Definition types.h:306
This file contains some common data types.
const char * get_codec_file_extension(codec_t codec)
uint32_t get_fourcc(codec_t codec)
static void * video_export_thread(void *arg)
void video_export(struct video_export *s, struct video_frame *frame)
#define MAX_QUEUE_SIZE
void output_summary(struct video_export *s)
void video_export_destroy(struct video_export *s)
struct video_export * video_export_init(const char *path)
#define VIDEO_EXPORT_SUMMARY_VERSION
bool video_desc_eq(struct video_desc a, struct video_desc b)
Compares two video descriptions.