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 <stdlib.h>
28#include <string.h>
29
30#include "containers/core/containers_private.h"
31#include "containers/core/containers_io_helpers.h"
32#include "containers/core/containers_utils.h"
33#include "containers/core/containers_index.h"
34#include "containers/core/containers_logging.h"
35
36/******************************************************************************
37Defines.
38******************************************************************************/
39
40#define LI32(b) (((b)[3]<<24)|((b)[2]<<16)|((b)[1]<<8)|((b)[0]))
41#define LI24(b) (((b)[2]<<16)|((b)[1]<<8)|((b)[0]))
42
43/******************************************************************************
44Type definitions
45******************************************************************************/
46typedef struct {
47 unsigned int num_frames : 24;
48 unsigned int constant_c5 : 8;
49 int constant_4;
50 uint32_t struct_c;
51 uint32_t vert_size;
52 uint32_t horiz_size;
53 int constant_c;
54 uint32_t struct_b[2];
55 uint32_t framerate;
56} RCV_FILE_HEADER_T;
57
58typedef struct {
59 unsigned int framesize : 24;
60 unsigned int res : 7;
61 unsigned int keyframe : 1;
62 uint32_t timestamp;
63} RCV_FRAME_HEADER_T;
64
65typedef struct VC_CONTAINER_MODULE_T
66{
67 VC_CONTAINER_TRACK_T *track;
68 uint8_t extradata[4];
69 uint8_t mid_frame;
70 uint32_t frame_read;
71 RCV_FRAME_HEADER_T frame;
72 VC_CONTAINER_INDEX_T *index; /* index of key frames */
73
74} VC_CONTAINER_MODULE_T;
75
76/******************************************************************************
77Function prototypes
78******************************************************************************/
79VC_CONTAINER_STATUS_T rcv_reader_open( VC_CONTAINER_T * );
80
81/******************************************************************************
82Local Functions
83******************************************************************************/
84
85static VC_CONTAINER_STATUS_T rcv_read_header(VC_CONTAINER_T *p_ctx)
86{
87 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
88 RCV_FILE_HEADER_T header;
89 uint8_t dummy[36];
90
91 if(PEEK_BYTES(p_ctx, dummy, sizeof(dummy)) != sizeof(dummy)) return VC_CONTAINER_ERROR_EOS;
92
93 header.num_frames = LI24(dummy);
94 header.constant_c5 = dummy[3];
95 header.constant_4 = LI32(dummy+4);
96
97 // extradata is just struct_c from the header
98 memcpy(module->extradata, dummy+8, 4);
99 module->track->format->extradata = module->extradata;
100 module->track->format->extradata_size = 4;
101
102 module->track->format->type->video.height = LI32(dummy+12);
103 module->track->format->type->video.width = LI32(dummy+16);
104
105 header.constant_c = LI32(dummy+20);
106 memcpy(header.struct_b, dummy+24, 8);
107 header.framerate = LI32(dummy+32);
108
109 if(header.constant_c5 != 0xc5 || header.constant_4 != 0x4 || header.constant_c != 0xc)
110 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
111
112 if(header.framerate != 0 && header.framerate != 0xffffffffUL)
113 {
114 module->track->format->type->video.frame_rate_num = header.framerate;
115 module->track->format->type->video.frame_rate_den = 1;
116 }
117
118 // fill in general information
119 if(header.num_frames != (1<<24)-1 && header.framerate != 0 && header.framerate != 0xffffffffUL)
120 p_ctx->duration = ((int64_t) header.num_frames * 1000000LL) / (int64_t) header.framerate;
121
122 // we're happy that this is an rcv file
123 SKIP_BYTES(p_ctx, sizeof(dummy));
124
125 return STREAM_STATUS(p_ctx);
126}
127
128/*****************************************************************************
129 * Utility function to seek to the keyframe nearest the given timestamp.
130 *
131 * @param p_ctx Pointer to the container context.
132 * @param timestamp The requested time. On success, this is updated with the time of the selected keyframe.
133 * @param later If true, the selected frame is the earliest keyframe with a time greater or equal to timestamp.
134 * If false, the selected frame is the latest keyframe with a time earlier or equal to timestamp.
135 * @return Status code.
136 */
137static VC_CONTAINER_STATUS_T rcv_seek_nearest_keyframe(VC_CONTAINER_T *p_ctx, int64_t *timestamp, int later)
138{
139 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
140 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
141 int64_t prev_keyframe_offset = sizeof(RCV_FILE_HEADER_T); /* set to very first frame */
142 int64_t prev_keyframe_timestamp = 0;
143 int use_prev_keyframe = !later;
144
145 if(use_prev_keyframe || (module->frame.timestamp * 1000LL > *timestamp))
146 {
147 /* A seek has been requested to an earlier keyframe, so rewind to the beginning
148 * of the stream since there's no information available on previous frames */
149 SEEK(p_ctx, sizeof(RCV_FILE_HEADER_T));
150 memset(&module->frame, 0, sizeof(RCV_FRAME_HEADER_T));
151 module->mid_frame = 0;
152 module->frame_read = 0;
153 }
154
155 if(module->mid_frame)
156 {
157 /* Seek back to the start of the current frame */
158 SEEK(p_ctx, STREAM_POSITION(p_ctx) - module->frame_read - sizeof(RCV_FILE_HEADER_T));
159 module->mid_frame = 0;
160 module->frame_read = 0;
161 }
162
163 while(1)
164 {
165 if(PEEK_BYTES(p_ctx, &module->frame, sizeof(RCV_FRAME_HEADER_T)) != sizeof(RCV_FRAME_HEADER_T))
166 {
167 status = VC_CONTAINER_ERROR_EOS;
168 break;
169 }
170
171 if(module->frame.keyframe)
172 {
173 if(module->index)
174 vc_container_index_add(module->index, module->frame.timestamp * 1000LL, STREAM_POSITION(p_ctx));
175
176 if((module->frame.timestamp * 1000LL) >= *timestamp)
177 {
178 if((module->frame.timestamp * 1000LL) == *timestamp)
179 use_prev_keyframe = 0;
180
181 *timestamp = module->frame.timestamp * 1000LL;
182
183 break;
184 }
185
186 prev_keyframe_offset = STREAM_POSITION(p_ctx);
187 prev_keyframe_timestamp = module->frame.timestamp * 1000LL;
188 }
189
190 SKIP_BYTES(p_ctx, module->frame.framesize + sizeof(RCV_FRAME_HEADER_T));
191 }
192
193 if(use_prev_keyframe)
194 {
195 *timestamp = prev_keyframe_timestamp;
196 status = SEEK(p_ctx, prev_keyframe_offset);
197 }
198
199 return status;
200}
201
202/*****************************************************************************
203Functions exported as part of the Container Module API
204*****************************************************************************/
205static VC_CONTAINER_STATUS_T rcv_reader_read( VC_CONTAINER_T *p_ctx,
206 VC_CONTAINER_PACKET_T *packet, uint32_t flags )
207{
208 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
209 unsigned int size;
210
211 if(!module->mid_frame)
212 {
213 /* Save the current position for updating the indexer */
214 int64_t position = STREAM_POSITION(p_ctx);
215
216 if(READ_BYTES(p_ctx, &module->frame, sizeof(RCV_FRAME_HEADER_T)) != sizeof(RCV_FRAME_HEADER_T))
217 return VC_CONTAINER_ERROR_EOS;
218 module->mid_frame = 1;
219 module->frame_read = 0;
220
221 if(module->index && module->frame.keyframe)
222 vc_container_index_add(module->index, (int64_t)module->frame.timestamp * 1000LL, position);
223 }
224
225 packet->size = module->frame.framesize;
226 packet->dts = packet->pts = module->frame.timestamp * 1000LL;
227 packet->track = 0;
228 packet->flags = 0;
229 if(module->frame_read == 0)
230 packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START;
231 if(module->frame.keyframe)
232 packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME;
233
234 if(flags & VC_CONTAINER_READ_FLAG_SKIP)
235 {
236 size = SKIP_BYTES(p_ctx, module->frame.framesize - module->frame_read);
237 if((module->frame_read += size) == module->frame.framesize)
238 {
239 module->frame_read = 0;
240 module->mid_frame = 0;
241 }
242 return STREAM_STATUS(p_ctx);
243 }
244
245 if(flags & VC_CONTAINER_READ_FLAG_INFO)
246 return VC_CONTAINER_SUCCESS;
247
248 size = MIN(module->frame.framesize - module->frame_read, packet->buffer_size);
249 size = READ_BYTES(p_ctx, packet->data, size);
250 if((module->frame_read += size) == module->frame.framesize)
251 {
252 module->frame_read = 0;
253 module->mid_frame = 0;
254 packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END;
255 }
256 packet->size = size;
257
258 return size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(p_ctx);
259}
260
261/*****************************************************************************/
262static VC_CONTAINER_STATUS_T rcv_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *offset,
263 VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags)
264{
265 int past = 1;
266 int64_t position;
267 int64_t timestamp = *offset;
268 VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FAILED;
269 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
270 VC_CONTAINER_PARAM_UNUSED(mode);
271
272 if(module->index)
273 status = vc_container_index_get(module->index, flags & VC_CONTAINER_SEEK_FLAG_FORWARD, &timestamp, &position, &past);
274
275 if(status == VC_CONTAINER_SUCCESS && !past)
276 {
277 /* Indexed keyframe found */
278 module->frame_read = 0;
279 module->mid_frame = 0;
280 *offset = timestamp;
281 status = SEEK(p_ctx, position);
282 }
283 else
284 {
285 /* No indexed keyframe found, so seek through all frames */
286 status = rcv_seek_nearest_keyframe(p_ctx, offset, flags & VC_CONTAINER_SEEK_FLAG_FORWARD);
287 }
288
289 return status;
290}
291
292/*****************************************************************************/
293static VC_CONTAINER_STATUS_T rcv_reader_close( VC_CONTAINER_T *p_ctx )
294{
295 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
296 for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--)
297 vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]);
298
299 if(module->index)
300 vc_container_index_free(module->index);
301
302 free(module);
303
304 return VC_CONTAINER_SUCCESS;
305}
306
307/*****************************************************************************/
308VC_CONTAINER_STATUS_T rcv_reader_open( VC_CONTAINER_T *p_ctx )
309{
310 VC_CONTAINER_MODULE_T *module = 0;
311 VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
312 uint8_t dummy[8];
313
314 /* Quick check for a valid file header */
315 if((PEEK_BYTES(p_ctx, dummy, sizeof(dummy)) != sizeof(dummy)) ||
316 dummy[3] != 0xc5 || LI32(dummy+4) != 0x4)
317 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
318
319 /* Allocate our context */
320 module = malloc(sizeof(*module));
321 if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
322 memset(module, 0, sizeof(*module));
323 p_ctx->priv->module = module;
324 p_ctx->tracks_num = 1;
325 p_ctx->tracks = &module->track;
326 p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0);
327 if(!p_ctx->tracks[0]) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
328 p_ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO;
329 p_ctx->tracks[0]->format->codec = VC_CONTAINER_CODEC_WMV3;
330 p_ctx->tracks[0]->is_enabled = true;
331
332 if((status = rcv_read_header(p_ctx)) != VC_CONTAINER_SUCCESS) goto error;
333
334 LOG_DEBUG(p_ctx, "using rcv reader");
335
336 if(vc_container_index_create(&module->index, 512) == VC_CONTAINER_SUCCESS)
337 vc_container_index_add(module->index, 0LL, STREAM_POSITION(p_ctx));
338
339 if(STREAM_SEEKABLE(p_ctx))
340 p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK;
341
342 p_ctx->priv->pf_close = rcv_reader_close;
343 p_ctx->priv->pf_read = rcv_reader_read;
344 p_ctx->priv->pf_seek = rcv_reader_seek;
345 return VC_CONTAINER_SUCCESS;
346
347 error:
348 if(module) rcv_reader_close(p_ctx);
349 return status;
350}
351
352/********************************************************************************
353 Entrypoint function
354********************************************************************************/
355
356#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
357# pragma weak reader_open rcv_reader_open
358#endif
359