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 <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 | /****************************************************************************** |
37 | Defines. |
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 | /****************************************************************************** |
44 | Type definitions |
45 | ******************************************************************************/ |
46 | typedef 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 | } ; |
57 | |
58 | typedef struct { |
59 | unsigned int framesize : 24; |
60 | unsigned int res : 7; |
61 | unsigned int keyframe : 1; |
62 | uint32_t timestamp; |
63 | } ; |
64 | |
65 | typedef struct VC_CONTAINER_MODULE_T |
66 | { |
67 | VC_CONTAINER_TRACK_T *track; |
68 | uint8_t [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 | /****************************************************************************** |
77 | Function prototypes |
78 | ******************************************************************************/ |
79 | VC_CONTAINER_STATUS_T rcv_reader_open( VC_CONTAINER_T * ); |
80 | |
81 | /****************************************************************************** |
82 | Local Functions |
83 | ******************************************************************************/ |
84 | |
85 | static VC_CONTAINER_STATUS_T (VC_CONTAINER_T *p_ctx) |
86 | { |
87 | VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
88 | RCV_FILE_HEADER_T ; |
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 | */ |
137 | static 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 | /***************************************************************************** |
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 ) |
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 | /*****************************************************************************/ |
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) |
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, ×tamp, &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 | /*****************************************************************************/ |
293 | static 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 | /*****************************************************************************/ |
308 | VC_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 | |