1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON 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
25SOFTWARE, 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/******************************************************************************
39Defines.
40******************************************************************************/
41#define FILE_HEADER_SIZE_MAX 1024
42#define FRAME_HEADER_SIZE_MAX 256
43#define OPTION_SIZE_MAX 32
44
45/******************************************************************************
46Type definitions
47******************************************************************************/
48typedef 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 frame_header;
58 unsigned int frame_header_size;
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/******************************************************************************
68Function prototypes
69******************************************************************************/
70VC_CONTAINER_STATUS_T rawvideo_reader_open( VC_CONTAINER_T * );
71
72/******************************************************************************
73Local Functions
74******************************************************************************/
75static 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
114static VC_CONTAINER_STATUS_T read_yuv4mpeg2_file_header( 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
176static VC_CONTAINER_STATUS_T read_yuv4mpeg2_frame_header( 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 header[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
209static 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/*****************************************************************************
258Functions exported as part of the Container Module API
259 *****************************************************************************/
260static 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/*****************************************************************************/
323static 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/*****************************************************************************/
350static 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/*****************************************************************************/
360VC_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