2 Copyright (c) 2012, Broadcom Europe Ltd
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.
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.
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"
36 /******************************************************************************
38 ******************************************************************************/
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]))
43 /******************************************************************************
45 ******************************************************************************/
47 unsigned int num_frames : 24;
48 unsigned int constant_c5 : 8;
59 unsigned int framesize : 24;
61 unsigned int keyframe : 1;
65 typedef struct VC_CONTAINER_MODULE_T
67 VC_CONTAINER_TRACK_T *track;
71 RCV_FRAME_HEADER_T frame;
72 VC_CONTAINER_INDEX_T *index; /* index of key frames */
74 } VC_CONTAINER_MODULE_T;
76 /******************************************************************************
78 ******************************************************************************/
79 VC_CONTAINER_STATUS_T rcv_reader_open( VC_CONTAINER_T * );
81 /******************************************************************************
83 ******************************************************************************/
85 static VC_CONTAINER_STATUS_T rcv_read_header(VC_CONTAINER_T *p_ctx)
87 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
88 RCV_FILE_HEADER_T header;
91 if(PEEK_BYTES(p_ctx, dummy, sizeof(dummy)) != sizeof(dummy)) return VC_CONTAINER_ERROR_EOS;
93 header.num_frames = LI24(dummy);
94 header.constant_c5 = dummy[3];
95 header.constant_4 = LI32(dummy+4);
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;
102 module->track->format->type->video.height = LI32(dummy+12);
103 module->track->format->type->video.width = LI32(dummy+16);
105 header.constant_c = LI32(dummy+20);
106 memcpy(header.struct_b, dummy+24, 8);
107 header.framerate = LI32(dummy+32);
109 if(header.constant_c5 != 0xc5 || header.constant_4 != 0x4 || header.constant_c != 0xc)
110 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
112 if(header.framerate != 0 && header.framerate != 0xffffffffUL)
114 module->track->format->type->video.frame_rate_num = header.framerate;
115 module->track->format->type->video.frame_rate_den = 1;
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;
122 // we're happy that this is an rcv file
123 SKIP_BYTES(p_ctx, sizeof(dummy));
125 return STREAM_STATUS(p_ctx);
128 /*****************************************************************************
129 * Utility function to seek to the keyframe nearest the given timestamp.
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.
137 static VC_CONTAINER_STATUS_T rcv_seek_nearest_keyframe(VC_CONTAINER_T *p_ctx, int64_t *timestamp, int later)
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;
145 if(use_prev_keyframe || (module->frame.timestamp * 1000LL > *timestamp))
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;
155 if(module->mid_frame)
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;
165 if(PEEK_BYTES(p_ctx, &module->frame, sizeof(RCV_FRAME_HEADER_T)) != sizeof(RCV_FRAME_HEADER_T))
167 status = VC_CONTAINER_ERROR_EOS;
171 if(module->frame.keyframe)
174 vc_container_index_add(module->index, module->frame.timestamp * 1000LL, STREAM_POSITION(p_ctx));
176 if((module->frame.timestamp * 1000LL) >= *timestamp)
178 if((module->frame.timestamp * 1000LL) == *timestamp)
179 use_prev_keyframe = 0;
181 *timestamp = module->frame.timestamp * 1000LL;
186 prev_keyframe_offset = STREAM_POSITION(p_ctx);
187 prev_keyframe_timestamp = module->frame.timestamp * 1000LL;
190 SKIP_BYTES(p_ctx, module->frame.framesize + sizeof(RCV_FRAME_HEADER_T));
193 if(use_prev_keyframe)
195 *timestamp = prev_keyframe_timestamp;
196 status = SEEK(p_ctx, prev_keyframe_offset);
202 /*****************************************************************************
203 Functions exported as part of the Container Module API
204 *****************************************************************************/
205 static VC_CONTAINER_STATUS_T rcv_reader_read( VC_CONTAINER_T *p_ctx,
206 VC_CONTAINER_PACKET_T *packet, uint32_t flags )
208 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
211 if(!module->mid_frame)
213 /* Save the current position for updating the indexer */
214 int64_t position = STREAM_POSITION(p_ctx);
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;
221 if(module->index && module->frame.keyframe)
222 vc_container_index_add(module->index, (int64_t)module->frame.timestamp * 1000LL, position);
225 packet->size = module->frame.framesize;
226 packet->dts = packet->pts = module->frame.timestamp * 1000LL;
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;
234 if(flags & VC_CONTAINER_READ_FLAG_SKIP)
236 size = SKIP_BYTES(p_ctx, module->frame.framesize - module->frame_read);
237 if((module->frame_read += size) == module->frame.framesize)
239 module->frame_read = 0;
240 module->mid_frame = 0;
242 return STREAM_STATUS(p_ctx);
245 if(flags & VC_CONTAINER_READ_FLAG_INFO)
246 return VC_CONTAINER_SUCCESS;
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)
252 module->frame_read = 0;
253 module->mid_frame = 0;
254 packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END;
258 return size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(p_ctx);
261 /*****************************************************************************/
262 static 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)
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);
273 status = vc_container_index_get(module->index, flags & VC_CONTAINER_SEEK_FLAG_FORWARD, ×tamp, &position, &past);
275 if(status == VC_CONTAINER_SUCCESS && !past)
277 /* Indexed keyframe found */
278 module->frame_read = 0;
279 module->mid_frame = 0;
281 status = SEEK(p_ctx, position);
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);
292 /*****************************************************************************/
293 static VC_CONTAINER_STATUS_T rcv_reader_close( VC_CONTAINER_T *p_ctx )
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]);
300 vc_container_index_free(module->index);
304 return VC_CONTAINER_SUCCESS;
307 /*****************************************************************************/
308 VC_CONTAINER_STATUS_T rcv_reader_open( VC_CONTAINER_T *p_ctx )
310 VC_CONTAINER_MODULE_T *module = 0;
311 VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
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;
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;
332 if((status = rcv_read_header(p_ctx)) != VC_CONTAINER_SUCCESS) goto error;
334 LOG_DEBUG(p_ctx, "using rcv reader");
336 if(vc_container_index_create(&module->index, 512) == VC_CONTAINER_SUCCESS)
337 vc_container_index_add(module->index, 0LL, STREAM_POSITION(p_ctx));
339 if(STREAM_SEEKABLE(p_ctx))
340 p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK;
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;
348 if(module) rcv_reader_close(p_ctx);
352 /********************************************************************************
354 ********************************************************************************/
356 #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
357 # pragma weak reader_open rcv_reader_open