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_logging.h"
34
35/******************************************************************************
36Defines.
37******************************************************************************/
38
39#define BI32(b) (((b)[0]<<24)|((b)[1]<<16)|((b)[2]<<8)|((b)[3]))
40#define BI16(b) (((b)[0]<<8)|((b)[1]))
41
42#define FRAME_HEADER_LEN 20
43#define MAX_NUM_SEGMENTS 64
44
45/******************************************************************************
46Type definitions
47******************************************************************************/
48typedef struct {
49 uint32_t len;
50 uint32_t timestamp;
51 uint16_t sequence;
52 uint16_t flags;
53 uint32_t num_segments;
54 uint32_t seg_offset;
55} RV9_FRAME_HEADER_T;
56
57typedef struct VC_CONTAINER_MODULE_T
58{
59 VC_CONTAINER_TRACK_T *track;
60 uint8_t mid_frame;
61 uint32_t frame_read;
62 uint32_t frame_len;
63 RV9_FRAME_HEADER_T hdr;
64 uint8_t data[FRAME_HEADER_LEN + (MAX_NUM_SEGMENTS<<3) + 1];
65 uint32_t data_len;
66 uint8_t type;
67} VC_CONTAINER_MODULE_T;
68
69/******************************************************************************
70Function prototypes
71******************************************************************************/
72VC_CONTAINER_STATUS_T rv9_reader_open( VC_CONTAINER_T * );
73
74/******************************************************************************
75Local Functions
76******************************************************************************/
77
78static VC_CONTAINER_STATUS_T rv9_read_file_header(VC_CONTAINER_T *p_ctx,
79 VC_CONTAINER_TRACK_T *track)
80{
81 VC_CONTAINER_STATUS_T status;
82 VC_CONTAINER_FOURCC_T codec;
83 uint8_t dummy[12];
84 uint32_t length;
85
86 if(PEEK_BYTES(p_ctx, dummy, sizeof(dummy)) != sizeof(dummy)) return VC_CONTAINER_ERROR_EOS;
87
88 length = BI32(dummy);
89 if(length < 12 || length > 1024) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
90
91 if(dummy[4] != 'V' || dummy[5] != 'I' || dummy[6] != 'D' || dummy[7] != 'O' ||
92 dummy[8] != 'R' || dummy[9] != 'V' || dummy[11] != '0')
93 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
94
95 switch(dummy[10]) {
96 case '4': codec = VC_CONTAINER_CODEC_RV40; break;
97 case '3': codec = VC_CONTAINER_CODEC_RV30; break;
98 case '2': codec = VC_CONTAINER_CODEC_RV20; break;
99 case '1': codec = VC_CONTAINER_CODEC_RV10; break;
100 default: return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
101 }
102
103 if (!track)
104 return VC_CONTAINER_SUCCESS;
105
106 status = vc_container_track_allocate_extradata(p_ctx, track, length);
107 if(status != VC_CONTAINER_SUCCESS) return status;
108
109 if(READ_BYTES(p_ctx, track->format->extradata, length) != length) return VC_CONTAINER_ERROR_EOS;
110 track->format->extradata_size = length;
111
112 track->format->codec = codec;
113 return STREAM_STATUS(p_ctx);
114}
115
116static VC_CONTAINER_STATUS_T rv9_read_frame_header(VC_CONTAINER_T *p_ctx)
117{
118 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
119 uint32_t seg_offset = (uint32_t) -1;
120 uint8_t *buffer = module->data + FRAME_HEADER_LEN;
121
122 if(READ_BYTES(p_ctx, module->data, FRAME_HEADER_LEN) != FRAME_HEADER_LEN) return VC_CONTAINER_ERROR_EOS;
123 module->data_len = FRAME_HEADER_LEN;
124
125 module->hdr.len = BI32(module->data);
126 module->hdr.timestamp = BI32(module->data+4);
127 module->hdr.sequence = BI16(module->data+8);
128 module->hdr.flags = BI16(module->data+10);
129 module->hdr.num_segments = BI32(module->data+16);
130
131 module->frame_len = FRAME_HEADER_LEN + (module->hdr.num_segments * 8) + module->hdr.len;
132
133 // if we have space, we store up the segments in memory so we can tell the frame
134 // type, since most streams have their type byte as the first follow the segment information.
135 // if we don't have space, then we just don't know the frame type, so will not emit timestamp
136 // information as we don't know if it's reliable.
137 if(module->hdr.num_segments <= MAX_NUM_SEGMENTS)
138 {
139 uint32_t i;
140
141 if(READ_BYTES(p_ctx, buffer, 8*module->hdr.num_segments) != 8*module->hdr.num_segments) return VC_CONTAINER_ERROR_EOS;
142 module->data_len += (module->hdr.num_segments * 8);
143
144 for (i=0; i<module->hdr.num_segments; i++)
145 {
146 uint32_t valid_seg;
147 uint32_t offset;
148
149 valid_seg = BI32(buffer);
150 offset = BI32(buffer+4);
151
152 if (valid_seg && seg_offset > offset)
153 seg_offset = offset;
154
155 // this boolean field should have only 0 or 1 values
156 if(valid_seg > 1) return VC_CONTAINER_ERROR_FORMAT_INVALID;
157
158 buffer += 8;
159 }
160 }
161
162 if(seg_offset == 0)
163 {
164 if (READ_BYTES(p_ctx, buffer, 1) != 1) return VC_CONTAINER_ERROR_EOS;
165 module->data_len += 1;
166
167 module->type = (*buffer >> 5) & 3;
168 }
169 else
170 module->type = (uint8_t) -1;
171
172 return VC_CONTAINER_SUCCESS;
173}
174
175static uint32_t rv9_get_frame_data(VC_CONTAINER_T *p_ctx, uint32_t len, uint8_t *dest)
176{
177 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
178 uint32_t ret = 0;
179
180 // we may have read some data before into the data array, so
181 // check whether we've copied all this data out first.
182 if(module->frame_read < module->data_len)
183 {
184 uint32_t copy = MIN(len, module->data_len - module->frame_read);
185 if(dest)
186 {
187 memcpy(dest, module->data + module->frame_read, copy);
188 dest += copy;
189 }
190 ret += copy;
191 len -= copy;
192 }
193
194 // if there is still more to do, we need to access the IO to do this.
195 if(len > 0)
196 {
197 if(dest)
198 ret += READ_BYTES(p_ctx, dest, len);
199 else
200 ret += SKIP_BYTES(p_ctx, len);
201 }
202
203 module->frame_read += ret;
204 return ret;
205}
206
207/*****************************************************************************
208Functions exported as part of the Container Module API
209*****************************************************************************/
210static VC_CONTAINER_STATUS_T rv9_reader_read( VC_CONTAINER_T *p_ctx,
211 VC_CONTAINER_PACKET_T *packet,
212 uint32_t flags )
213{
214 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
215 VC_CONTAINER_STATUS_T status;
216 unsigned int size;
217
218 if(!module->mid_frame)
219 {
220 if((status = rv9_read_frame_header(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
221
222 module->mid_frame = 1;
223 module->frame_read = 0;
224 }
225
226 packet->size = module->frame_len;
227 packet->pts = module->type < 3 ? module->hdr.timestamp * 1000LL : VC_CONTAINER_TIME_UNKNOWN;
228 packet->dts = packet->pts;
229 packet->track = 0;
230 packet->flags = module->type < 2 ? VC_CONTAINER_PACKET_FLAG_KEYFRAME : 0;
231 if(module->frame_read == 0)
232 packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START;
233
234 if(flags & VC_CONTAINER_READ_FLAG_SKIP)
235 {
236 size = rv9_get_frame_data(p_ctx, module->frame_len - module->frame_read, NULL);
237 if(module->frame_read == module->frame_len)
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_len - module->frame_read, packet->buffer_size);
249 size = rv9_get_frame_data(p_ctx, size, packet->data);
250 if(module->frame_read == module->frame_len)
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 rv9_reader_seek( VC_CONTAINER_T *p_ctx,
263 int64_t *offset,
264 VC_CONTAINER_SEEK_MODE_T mode,
265 VC_CONTAINER_SEEK_FLAGS_T flags)
266{
267 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
268 VC_CONTAINER_PARAM_UNUSED(flags);
269
270 if(*offset == 0LL && mode == VC_CONTAINER_SEEK_MODE_TIME)
271 {
272 SEEK(p_ctx, module->track->format->extradata_size);
273 module->mid_frame = 0;
274 module->frame_read = 0;
275 return STREAM_STATUS(p_ctx);
276 }
277 else
278 return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
279}
280
281/*****************************************************************************/
282static VC_CONTAINER_STATUS_T rv9_reader_close( VC_CONTAINER_T *p_ctx )
283{
284 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
285 for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--)
286 vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]);
287 free(module);
288 return VC_CONTAINER_SUCCESS;
289}
290
291/*****************************************************************************/
292VC_CONTAINER_STATUS_T rv9_reader_open( VC_CONTAINER_T *p_ctx )
293{
294 VC_CONTAINER_MODULE_T *module = 0;
295 VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
296
297 /* Check the file header */
298 if(rv9_read_file_header(p_ctx, 0) != VC_CONTAINER_SUCCESS)
299 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
300
301 /* Allocate our context */
302 module = malloc(sizeof(*module));
303 if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
304 memset(module, 0, sizeof(*module));
305 p_ctx->priv->module = module;
306 p_ctx->tracks_num = 1;
307 p_ctx->tracks = &module->track;
308 p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0);
309 if(!p_ctx->tracks[0]) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
310 p_ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO;
311 p_ctx->tracks[0]->format->codec = VC_CONTAINER_CODEC_RV40;
312 p_ctx->tracks[0]->is_enabled = true;
313
314 if((status = rv9_read_file_header(p_ctx, p_ctx->tracks[0])) != VC_CONTAINER_SUCCESS) goto error;
315
316 LOG_DEBUG(p_ctx, "using rv9 reader");
317
318 p_ctx->priv->pf_close = rv9_reader_close;
319 p_ctx->priv->pf_read = rv9_reader_read;
320 p_ctx->priv->pf_seek = rv9_reader_seek;
321 return VC_CONTAINER_SUCCESS;
322
323 error:
324 LOG_DEBUG(p_ctx, "rv9: error opening stream (%i)", status);
325 if(module) rv9_reader_close(p_ctx);
326 return status;
327}
328
329/********************************************************************************
330 Entrypoint function
331********************************************************************************/
332
333#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
334# pragma weak reader_open rv9_reader_open
335#endif
336