1 | /* |
2 | Copyright (c) 2012, Broadcom Europe Ltd |
3 | All rights reserved. |
4 | |
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the following conditions are met: |
7 | * Redistributions of source code must retain the above copyright |
8 | notice, this list of conditions and the following disclaimer. |
9 | * Redistributions in binary form must reproduce the above copyright |
10 | notice, this list of conditions and the following disclaimer in the |
11 | documentation and/or other materials provided with the distribution. |
12 | * Neither the name of the copyright holder nor the |
13 | names of its contributors may be used to endorse or promote products |
14 | derived from this software without specific prior written permission. |
15 | |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ |
27 | #include <stdio.h> |
28 | #include <stdlib.h> |
29 | #include <string.h> |
30 | |
31 | #include "containers/core/containers_private.h" |
32 | #include "containers/core/containers_io_helpers.h" |
33 | #include "containers/core/containers_utils.h" |
34 | #include "containers/core/containers_logging.h" |
35 | |
36 | #include "raw_video_common.h" |
37 | |
38 | /****************************************************************************** |
39 | Defines. |
40 | ******************************************************************************/ |
41 | #define 1024 |
42 | #define 256 |
43 | #define OPTION_SIZE_MAX 32 |
44 | |
45 | /****************************************************************************** |
46 | Type definitions |
47 | ******************************************************************************/ |
48 | typedef struct VC_CONTAINER_MODULE_T |
49 | { |
50 | VC_CONTAINER_TRACK_T *track; |
51 | VC_CONTAINER_STATUS_T status; |
52 | |
53 | bool yuv4mpeg2; |
54 | bool non_standard; |
55 | char option[OPTION_SIZE_MAX]; |
56 | |
57 | bool ; |
58 | unsigned int ; |
59 | |
60 | int64_t data_offset; |
61 | unsigned int block_size; |
62 | unsigned int block_offset; |
63 | unsigned int frames; |
64 | |
65 | } VC_CONTAINER_MODULE_T; |
66 | |
67 | /****************************************************************************** |
68 | Function prototypes |
69 | ******************************************************************************/ |
70 | VC_CONTAINER_STATUS_T rawvideo_reader_open( VC_CONTAINER_T * ); |
71 | |
72 | /****************************************************************************** |
73 | Local Functions |
74 | ******************************************************************************/ |
75 | static VC_CONTAINER_STATUS_T read_yuv4mpeg2_option( VC_CONTAINER_T *ctx, |
76 | unsigned int *bytes_left ) |
77 | { |
78 | VC_CONTAINER_MODULE_T *module = ctx->priv->module; |
79 | unsigned int size, i; |
80 | |
81 | /* Start by skipping spaces */ |
82 | while (*bytes_left && PEEK_U8(ctx) == ' ') |
83 | (*bytes_left)--, _SKIP_U8(ctx); |
84 | |
85 | size = PEEK_BYTES(ctx, module->option, |
86 | MIN(sizeof(module->option), *bytes_left)); |
87 | |
88 | /* The config option ends at next space or newline */ |
89 | for (i = 0; i < size; i++) |
90 | { |
91 | if (module->option[i] == ' ' || module->option[i] == 0x0a) |
92 | { |
93 | module->option[i] = 0; |
94 | break; |
95 | } |
96 | } |
97 | if (i == 0) |
98 | return VC_CONTAINER_ERROR_NOT_FOUND; |
99 | |
100 | *bytes_left -= i; |
101 | SKIP_BYTES(ctx, i); |
102 | |
103 | /* If option is too long, we just discard it */ |
104 | if (i == size) |
105 | { |
106 | while (*bytes_left && PEEK_U8(ctx) != ' ' && PEEK_U8(ctx) != 0x0a) |
107 | (*bytes_left)--, _SKIP_U8(ctx); |
108 | return VC_CONTAINER_ERROR_NOT_FOUND; |
109 | } |
110 | |
111 | return VC_CONTAINER_SUCCESS; |
112 | } |
113 | |
114 | static VC_CONTAINER_STATUS_T ( VC_CONTAINER_T *ctx ) |
115 | { |
116 | VC_CONTAINER_MODULE_T *module = ctx->priv->module; |
117 | unsigned int bytes_left = FILE_HEADER_SIZE_MAX - 10; |
118 | unsigned int value1, value2; |
119 | char codec[OPTION_SIZE_MAX] = "420" ; |
120 | uint8_t h[10]; |
121 | |
122 | /* Check for the YUV4MPEG2 signature */ |
123 | if (READ_BYTES(ctx, h, sizeof(h)) != sizeof(h)) |
124 | return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
125 | |
126 | if (memcmp(h, "YUV4MPEG2 " , sizeof(h))) |
127 | return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
128 | |
129 | /* Parse parameters */ |
130 | while (read_yuv4mpeg2_option(ctx, &bytes_left) == VC_CONTAINER_SUCCESS) |
131 | { |
132 | if (sscanf(module->option, "W%i" , &value1) == 1) |
133 | ctx->tracks[0]->format->type->video.width = value1; |
134 | else if (sscanf(module->option, "H%i" , &value1) == 1) |
135 | ctx->tracks[0]->format->type->video.height = value1; |
136 | else if (sscanf(module->option, "S%i" , &value1) == 1) |
137 | module->block_size = value1; |
138 | else if (sscanf(module->option, "F%i:%i" , &value1, &value2) == 2) |
139 | { |
140 | ctx->tracks[0]->format->type->video.frame_rate_num = value1; |
141 | ctx->tracks[0]->format->type->video.frame_rate_den = value2; |
142 | } |
143 | else if (sscanf(module->option, "A%i:%i" , &value1, &value2) == 2) |
144 | { |
145 | ctx->tracks[0]->format->type->video.par_num = value1; |
146 | ctx->tracks[0]->format->type->video.par_den = value2; |
147 | } |
148 | else if (module->option[0] == 'C') |
149 | { |
150 | strcpy(codec, module->option+1); |
151 | } |
152 | } |
153 | |
154 | /* Check the end marker */ |
155 | if (_READ_U8(ctx) != 0x0a) |
156 | { |
157 | LOG_ERROR(ctx, "missing end of header marker" ); |
158 | return VC_CONTAINER_ERROR_CORRUPTED; |
159 | } |
160 | |
161 | /* Find out which codec we are dealing with */ |
162 | if (from_yuv4mpeg2(codec, &ctx->tracks[0]->format->codec, &value1, &value2)) |
163 | { |
164 | module->block_size = ctx->tracks[0]->format->type->video.width * |
165 | ctx->tracks[0]->format->type->video.height * value1 / value2; |
166 | } |
167 | else |
168 | { |
169 | memcpy(&ctx->tracks[0]->format->codec, codec, 4); |
170 | module->non_standard = true; |
171 | } |
172 | |
173 | return VC_CONTAINER_SUCCESS; |
174 | } |
175 | |
176 | static VC_CONTAINER_STATUS_T ( VC_CONTAINER_T *ctx ) |
177 | { |
178 | VC_CONTAINER_MODULE_T *module = ctx->priv->module; |
179 | unsigned int bytes_left = FRAME_HEADER_SIZE_MAX - 5; |
180 | unsigned int value1; |
181 | char [5]; |
182 | |
183 | if (READ_BYTES(ctx, header, sizeof(header)) != sizeof(header) || |
184 | memcmp(header, "FRAME" , sizeof(header))) |
185 | { |
186 | LOG_ERROR(ctx, "missing frame marker" ); |
187 | return STREAM_STATUS(ctx) != VC_CONTAINER_SUCCESS ? |
188 | STREAM_STATUS(ctx) : VC_CONTAINER_ERROR_CORRUPTED; |
189 | } |
190 | |
191 | /* Parse parameters */ |
192 | while (read_yuv4mpeg2_option(ctx, &bytes_left) == VC_CONTAINER_SUCCESS) |
193 | { |
194 | if (module->non_standard && sscanf(module->option, "S%i" , &value1) == 1) |
195 | module->block_size = value1; |
196 | } |
197 | |
198 | /* Check the end marker */ |
199 | if (_READ_U8(ctx) != 0x0a) |
200 | { |
201 | LOG_ERROR(ctx, "missing end of frame header marker" ); |
202 | return VC_CONTAINER_ERROR_CORRUPTED; |
203 | } |
204 | |
205 | module->frame_header_size = FRAME_HEADER_SIZE_MAX - bytes_left - 1; |
206 | return VC_CONTAINER_SUCCESS; |
207 | } |
208 | |
209 | static VC_CONTAINER_STATUS_T rawvideo_parse_uri( VC_CONTAINER_T *ctx, |
210 | VC_CONTAINER_FOURCC_T *c, unsigned int *w, unsigned int *h, |
211 | unsigned int *fr_num, unsigned int *fr_den, unsigned *block_size ) |
212 | { |
213 | VC_CONTAINER_FOURCC_T codec = 0; |
214 | unsigned int i, matches, width = 0, height = 0, fn = 0, fd = 0, size = 0; |
215 | const char *uri = ctx->priv->io->uri; |
216 | |
217 | /* Try and find a match for the string describing the format */ |
218 | for (i = 0; uri[i]; i++) |
219 | { |
220 | if (uri[i] != '_' && uri[i+1] != 'C') |
221 | continue; |
222 | |
223 | matches = sscanf(uri+i, "_C%4cW%iH%iF%i#%iS%i" , (char *)&codec, |
224 | &width, &height, &fn, &fd, &size); |
225 | if (matches >= 3) |
226 | break; |
227 | } |
228 | if (!uri[i]) |
229 | return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
230 | |
231 | if (!size) |
232 | { |
233 | switch (codec) |
234 | { |
235 | case VC_CONTAINER_CODEC_I420: |
236 | case VC_CONTAINER_CODEC_YV12: |
237 | size = width * height * 3 / 2; |
238 | break; |
239 | default: break; |
240 | } |
241 | } |
242 | |
243 | if (!width || !height || !size) |
244 | return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
245 | |
246 | if (block_size) *block_size = size; |
247 | if (c) *c = codec; |
248 | if (w) *w = width; |
249 | if (h) *h = height; |
250 | if (fr_num) *fr_num = fn; |
251 | if (fr_den) *fr_den = fd; |
252 | if (block_size) *block_size = size; |
253 | |
254 | return VC_CONTAINER_SUCCESS; |
255 | } |
256 | |
257 | /***************************************************************************** |
258 | Functions exported as part of the Container Module API |
259 | *****************************************************************************/ |
260 | static VC_CONTAINER_STATUS_T rawvideo_reader_read( VC_CONTAINER_T *ctx, |
261 | VC_CONTAINER_PACKET_T *packet, uint32_t flags ) |
262 | { |
263 | VC_CONTAINER_MODULE_T *module = ctx->priv->module; |
264 | unsigned int size; |
265 | |
266 | if (module->status != VC_CONTAINER_SUCCESS) |
267 | return module->status; |
268 | |
269 | if (module->yuv4mpeg2 && !module->block_offset && |
270 | !module->frame_header) |
271 | { |
272 | module->status = read_yuv4mpeg2_frame_header(ctx); |
273 | if (module->status != VC_CONTAINER_SUCCESS) |
274 | return module->status; |
275 | |
276 | module->frame_header = true; |
277 | } |
278 | |
279 | if (!module->block_offset) |
280 | packet->pts = packet->dts = module->frames * INT64_C(1000000) * |
281 | ctx->tracks[0]->format->type->video.frame_rate_den / |
282 | ctx->tracks[0]->format->type->video.frame_rate_num; |
283 | else |
284 | packet->pts = packet->dts = VC_CONTAINER_TIME_UNKNOWN; |
285 | packet->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END | |
286 | VC_CONTAINER_PACKET_FLAG_KEYFRAME; |
287 | if (!module->block_offset) |
288 | packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; |
289 | packet->frame_size = module->block_size; |
290 | packet->size = module->block_size - module->block_offset; |
291 | packet->track = 0; |
292 | |
293 | if (flags & VC_CONTAINER_READ_FLAG_SKIP) |
294 | { |
295 | size = SKIP_BYTES(ctx, packet->size); |
296 | module->block_offset = 0; |
297 | module->frames++; |
298 | module->frame_header = 0; |
299 | module->status = STREAM_STATUS(ctx); |
300 | return module->status; |
301 | } |
302 | |
303 | if (flags & VC_CONTAINER_READ_FLAG_INFO) |
304 | return VC_CONTAINER_SUCCESS; |
305 | |
306 | size = MIN(module->block_size - module->block_offset, packet->buffer_size); |
307 | size = READ_BYTES(ctx, packet->data, size); |
308 | module->block_offset += size; |
309 | packet->size = size; |
310 | |
311 | if (module->block_offset == module->block_size) |
312 | { |
313 | module->block_offset = 0; |
314 | module->frame_header = 0; |
315 | module->frames++; |
316 | } |
317 | |
318 | module->status = size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(ctx); |
319 | return module->status; |
320 | } |
321 | |
322 | /*****************************************************************************/ |
323 | static VC_CONTAINER_STATUS_T rawvideo_reader_seek( VC_CONTAINER_T *ctx, int64_t *offset, |
324 | VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) |
325 | { |
326 | VC_CONTAINER_MODULE_T *module = ctx->priv->module; |
327 | VC_CONTAINER_PARAM_UNUSED(mode); |
328 | |
329 | module->frames = *offset * |
330 | ctx->tracks[0]->format->type->video.frame_rate_num / |
331 | ctx->tracks[0]->format->type->video.frame_rate_den / INT64_C(1000000); |
332 | module->block_offset = 0; |
333 | |
334 | if ((flags & VC_CONTAINER_SEEK_FLAG_FORWARD) && |
335 | module->frames * INT64_C(1000000) * |
336 | ctx->tracks[0]->format->type->video.frame_rate_den / |
337 | ctx->tracks[0]->format->type->video.frame_rate_num < *offset) |
338 | module->frames++; |
339 | |
340 | module->frame_header = 0; |
341 | |
342 | module->status = |
343 | SEEK(ctx, module->data_offset + module->frames * |
344 | (module->block_size + module->frame_header_size)); |
345 | |
346 | return module->status; |
347 | } |
348 | |
349 | /*****************************************************************************/ |
350 | static VC_CONTAINER_STATUS_T rawvideo_reader_close( VC_CONTAINER_T *ctx ) |
351 | { |
352 | VC_CONTAINER_MODULE_T *module = ctx->priv->module; |
353 | for (; ctx->tracks_num > 0; ctx->tracks_num--) |
354 | vc_container_free_track(ctx, ctx->tracks[ctx->tracks_num-1]); |
355 | free(module); |
356 | return VC_CONTAINER_SUCCESS; |
357 | } |
358 | |
359 | /*****************************************************************************/ |
360 | VC_CONTAINER_STATUS_T rawvideo_reader_open( VC_CONTAINER_T *ctx ) |
361 | { |
362 | VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; |
363 | const char *extension = vc_uri_path_extension(ctx->priv->uri); |
364 | VC_CONTAINER_MODULE_T *module = 0; |
365 | bool yuv4mpeg2 = false; |
366 | uint8_t h[10]; |
367 | |
368 | /* Check if the user has specified a container */ |
369 | vc_uri_find_query(ctx->priv->uri, 0, "container" , &extension); |
370 | |
371 | /* Check for the YUV4MPEG2 signature */ |
372 | if (PEEK_BYTES(ctx, h, sizeof(h)) != sizeof(h)) |
373 | return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
374 | if (!memcmp(h, "YUV4MPEG2 " , sizeof(h))) |
375 | yuv4mpeg2 = true; |
376 | |
377 | /* Or check if the extension is supported */ |
378 | if (!yuv4mpeg2 && |
379 | !(extension && !strcasecmp(extension, "yuv" ))) |
380 | return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
381 | |
382 | LOG_DEBUG(ctx, "using raw video reader" ); |
383 | |
384 | /* Allocate our context */ |
385 | module = malloc(sizeof(*module)); |
386 | if (!module) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; |
387 | memset(module, 0, sizeof(*module)); |
388 | ctx->priv->module = module; |
389 | ctx->tracks_num = 1; |
390 | ctx->tracks = &module->track; |
391 | ctx->tracks[0] = vc_container_allocate_track(ctx, 0); |
392 | if (!ctx->tracks[0]) |
393 | { |
394 | status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; |
395 | goto error; |
396 | } |
397 | ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; |
398 | ctx->tracks[0]->is_enabled = true; |
399 | ctx->tracks[0]->format->type->video.frame_rate_num = 25; |
400 | ctx->tracks[0]->format->type->video.frame_rate_den = 1; |
401 | ctx->tracks[0]->format->type->video.par_num = 1; |
402 | ctx->tracks[0]->format->type->video.par_den = 1; |
403 | |
404 | if (yuv4mpeg2) |
405 | { |
406 | status = read_yuv4mpeg2_file_header(ctx); |
407 | if (status != VC_CONTAINER_SUCCESS) |
408 | goto error; |
409 | |
410 | module->data_offset = STREAM_POSITION(ctx); |
411 | |
412 | status = read_yuv4mpeg2_frame_header(ctx); |
413 | if (status != VC_CONTAINER_SUCCESS) |
414 | goto error; |
415 | module->frame_header = true; |
416 | } |
417 | else |
418 | { |
419 | VC_CONTAINER_FOURCC_T codec; |
420 | unsigned int width, height, fr_num, fr_den, block_size; |
421 | |
422 | status = rawvideo_parse_uri(ctx, &codec, &width, &height, |
423 | &fr_num, &fr_den, &block_size); |
424 | if (status != VC_CONTAINER_SUCCESS) |
425 | goto error; |
426 | ctx->tracks[0]->format->codec = codec; |
427 | ctx->tracks[0]->format->type->video.width = width; |
428 | ctx->tracks[0]->format->type->video.height = height; |
429 | if (fr_num && fr_den) |
430 | { |
431 | ctx->tracks[0]->format->type->video.frame_rate_num = fr_num; |
432 | ctx->tracks[0]->format->type->video.frame_rate_den = fr_den; |
433 | } |
434 | module->block_size = block_size; |
435 | } |
436 | |
437 | /* |
438 | * We now have all the information we really need to start playing the stream |
439 | */ |
440 | |
441 | LOG_INFO(ctx, "rawvideo %4.4s/%ix%i/fps:%i:%i/size:%i" , |
442 | (char *)&ctx->tracks[0]->format->codec, |
443 | ctx->tracks[0]->format->type->video.width, |
444 | ctx->tracks[0]->format->type->video.height, |
445 | ctx->tracks[0]->format->type->video.frame_rate_num, |
446 | ctx->tracks[0]->format->type->video.frame_rate_den, module->block_size); |
447 | ctx->priv->pf_close = rawvideo_reader_close; |
448 | ctx->priv->pf_read = rawvideo_reader_read; |
449 | ctx->priv->pf_seek = rawvideo_reader_seek; |
450 | module->yuv4mpeg2 = yuv4mpeg2; |
451 | return VC_CONTAINER_SUCCESS; |
452 | |
453 | error: |
454 | LOG_DEBUG(ctx, "rawvideo: error opening stream (%i)" , status); |
455 | rawvideo_reader_close(ctx); |
456 | return status; |
457 | } |
458 | |
459 | /******************************************************************************** |
460 | Entrypoint function |
461 | ********************************************************************************/ |
462 | |
463 | #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) |
464 | # pragma weak reader_open rawvideo_reader_open |
465 | #endif |
466 | |