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 | |
28 | #include "mmal.h" |
29 | #include "core/mmal_component_private.h" |
30 | #include "core/mmal_port_private.h" |
31 | #include "mmal_logging.h" |
32 | |
33 | #include "containers/containers.h" |
34 | #include "containers/containers_codecs.h" |
35 | #include "containers/core/containers_utils.h" |
36 | |
37 | #define READER_MAX_URI_LENGTH 1024 |
38 | |
39 | #define WRITER_PORTS_NUM 3 /**< 3 ports should be enough for video + audio + subpicture */ |
40 | |
41 | /* Buffering requirements */ |
42 | #define READER_MIN_BUFFER_SIZE (2*1024) |
43 | #define READER_MIN_BUFFER_NUM 1 |
44 | #define READER_RECOMMENDED_BUFFER_SIZE (32*1024) |
45 | #define READER_RECOMMENDED_BUFFER_NUM 10 |
46 | |
47 | /*****************************************************************************/ |
48 | |
49 | /** Private context for this component */ |
50 | typedef struct MMAL_COMPONENT_MODULE_T |
51 | { |
52 | VC_CONTAINER_T *container; |
53 | char uri[READER_MAX_URI_LENGTH+1]; |
54 | unsigned int ports; |
55 | |
56 | MMAL_BOOL_T writer; |
57 | MMAL_BOOL_T error; |
58 | |
59 | /* Reader specific */ |
60 | MMAL_BOOL_T packet_logged; |
61 | |
62 | /* Writer specific */ |
63 | unsigned int port_last_used; |
64 | unsigned int port_writing_frame; |
65 | |
66 | } MMAL_COMPONENT_MODULE_T; |
67 | |
68 | typedef struct MMAL_PORT_MODULE_T |
69 | { |
70 | unsigned int track; |
71 | MMAL_QUEUE_T *queue; |
72 | |
73 | MMAL_BOOL_T flush; |
74 | MMAL_BOOL_T eos; |
75 | |
76 | VC_CONTAINER_ES_FORMAT_T *format; /**< Format description for the elementary stream */ |
77 | |
78 | } MMAL_PORT_MODULE_T; |
79 | |
80 | /*****************************************************************************/ |
81 | static struct { |
82 | VC_CONTAINER_FOURCC_T codec; |
83 | MMAL_FOURCC_T encoding; |
84 | VC_CONTAINER_FOURCC_T codec_variant; |
85 | MMAL_FOURCC_T encoding_variant; |
86 | } encoding_table[] = |
87 | { |
88 | {VC_CONTAINER_CODEC_H263, MMAL_ENCODING_H263, 0, 0}, |
89 | {VC_CONTAINER_CODEC_H264, MMAL_ENCODING_H264, 0, 0}, |
90 | {VC_CONTAINER_CODEC_H264, MMAL_ENCODING_H264, |
91 | VC_CONTAINER_VARIANT_H264_AVC1, MMAL_ENCODING_VARIANT_H264_AVC1}, |
92 | {VC_CONTAINER_CODEC_H264, MMAL_ENCODING_H264, |
93 | VC_CONTAINER_VARIANT_H264_RAW, MMAL_ENCODING_VARIANT_H264_RAW}, |
94 | {VC_CONTAINER_CODEC_MP4V, MMAL_ENCODING_MP4V, 0, 0}, |
95 | {VC_CONTAINER_CODEC_MP2V, MMAL_ENCODING_MP2V, 0, 0}, |
96 | {VC_CONTAINER_CODEC_MP1V, MMAL_ENCODING_MP1V, 0, 0}, |
97 | {VC_CONTAINER_CODEC_WMV3, MMAL_ENCODING_WMV3, 0, 0}, |
98 | {VC_CONTAINER_CODEC_WMV2, MMAL_ENCODING_WMV2, 0, 0}, |
99 | {VC_CONTAINER_CODEC_WMV1, MMAL_ENCODING_WMV1, 0, 0}, |
100 | {VC_CONTAINER_CODEC_WVC1, MMAL_ENCODING_WVC1, 0, 0}, |
101 | {VC_CONTAINER_CODEC_VP6, MMAL_ENCODING_VP6, 0, 0}, |
102 | {VC_CONTAINER_CODEC_VP7, MMAL_ENCODING_VP7, 0, 0}, |
103 | {VC_CONTAINER_CODEC_VP8, MMAL_ENCODING_VP8, 0, 0}, |
104 | {VC_CONTAINER_CODEC_THEORA, MMAL_ENCODING_THEORA, 0, 0}, |
105 | {VC_CONTAINER_CODEC_SPARK, MMAL_ENCODING_SPARK, 0, 0}, |
106 | |
107 | {VC_CONTAINER_CODEC_GIF, MMAL_ENCODING_GIF, 0, 0}, |
108 | {VC_CONTAINER_CODEC_JPEG, MMAL_ENCODING_JPEG, 0, 0}, |
109 | {VC_CONTAINER_CODEC_PNG, MMAL_ENCODING_PNG, 0, 0}, |
110 | {VC_CONTAINER_CODEC_PPM, MMAL_ENCODING_PPM, 0, 0}, |
111 | {VC_CONTAINER_CODEC_TGA, MMAL_ENCODING_TGA, 0, 0}, |
112 | {VC_CONTAINER_CODEC_BMP, MMAL_ENCODING_BMP, 0, 0}, |
113 | |
114 | {VC_CONTAINER_CODEC_PCM_SIGNED_BE, MMAL_ENCODING_PCM_SIGNED_BE, 0, 0}, |
115 | {VC_CONTAINER_CODEC_PCM_UNSIGNED_BE,MMAL_ENCODING_PCM_UNSIGNED_BE, 0, 0}, |
116 | {VC_CONTAINER_CODEC_PCM_SIGNED_LE, MMAL_ENCODING_PCM_SIGNED_LE, 0, 0}, |
117 | {VC_CONTAINER_CODEC_PCM_UNSIGNED_LE,MMAL_ENCODING_PCM_UNSIGNED_LE, 0, 0}, |
118 | {VC_CONTAINER_CODEC_PCM_FLOAT_BE, MMAL_ENCODING_PCM_FLOAT_BE, 0, 0}, |
119 | {VC_CONTAINER_CODEC_PCM_FLOAT_LE, MMAL_ENCODING_PCM_FLOAT_LE, 0, 0}, |
120 | |
121 | {VC_CONTAINER_CODEC_MPGA, MMAL_ENCODING_MPGA, 0, 0}, |
122 | {VC_CONTAINER_CODEC_MP4A, MMAL_ENCODING_MP4A, 0, 0}, |
123 | {VC_CONTAINER_CODEC_ALAW, MMAL_ENCODING_ALAW, 0, 0}, |
124 | {VC_CONTAINER_CODEC_MULAW, MMAL_ENCODING_MULAW, 0, 0}, |
125 | {VC_CONTAINER_CODEC_ADPCM_MS, MMAL_ENCODING_ADPCM_MS, 0, 0}, |
126 | {VC_CONTAINER_CODEC_ADPCM_IMA_MS, MMAL_ENCODING_ADPCM_IMA_MS, 0, 0}, |
127 | {VC_CONTAINER_CODEC_ADPCM_SWF, MMAL_ENCODING_ADPCM_SWF, 0, 0}, |
128 | {VC_CONTAINER_CODEC_WMA1, MMAL_ENCODING_WMA1, 0, 0}, |
129 | {VC_CONTAINER_CODEC_WMA2, MMAL_ENCODING_WMA2, 0, 0}, |
130 | {VC_CONTAINER_CODEC_WMAP, MMAL_ENCODING_WMAP, 0, 0}, |
131 | {VC_CONTAINER_CODEC_WMAL, MMAL_ENCODING_WMAL, 0, 0}, |
132 | {VC_CONTAINER_CODEC_WMAV, MMAL_ENCODING_WMAV, 0, 0}, |
133 | {VC_CONTAINER_CODEC_AMRNB, MMAL_ENCODING_AMRNB, 0, 0}, |
134 | {VC_CONTAINER_CODEC_AMRWB, MMAL_ENCODING_AMRWB, 0, 0}, |
135 | {VC_CONTAINER_CODEC_AMRWBP, MMAL_ENCODING_AMRWBP, 0, 0}, |
136 | {VC_CONTAINER_CODEC_AC3, MMAL_ENCODING_AC3, 0, 0}, |
137 | {VC_CONTAINER_CODEC_EAC3, MMAL_ENCODING_EAC3, 0, 0}, |
138 | {VC_CONTAINER_CODEC_DTS, MMAL_ENCODING_DTS, 0, 0}, |
139 | {VC_CONTAINER_CODEC_MLP, MMAL_ENCODING_MLP, 0, 0}, |
140 | {VC_CONTAINER_CODEC_FLAC, MMAL_ENCODING_FLAC, 0, 0}, |
141 | {VC_CONTAINER_CODEC_VORBIS, MMAL_ENCODING_VORBIS, 0, 0}, |
142 | {VC_CONTAINER_CODEC_SPEEX, MMAL_ENCODING_SPEEX, 0, 0}, |
143 | {VC_CONTAINER_CODEC_ATRAC3, MMAL_ENCODING_ATRAC3, 0, 0}, |
144 | {VC_CONTAINER_CODEC_ATRACX, MMAL_ENCODING_ATRACX, 0, 0}, |
145 | {VC_CONTAINER_CODEC_ATRACL, MMAL_ENCODING_ATRACL, 0, 0}, |
146 | {VC_CONTAINER_CODEC_MIDI, MMAL_ENCODING_MIDI, 0, 0}, |
147 | {VC_CONTAINER_CODEC_EVRC, MMAL_ENCODING_EVRC, 0, 0}, |
148 | {VC_CONTAINER_CODEC_NELLYMOSER, MMAL_ENCODING_NELLYMOSER, 0, 0}, |
149 | {VC_CONTAINER_CODEC_QCELP, MMAL_ENCODING_QCELP, 0, 0}, |
150 | |
151 | {VC_CONTAINER_CODEC_UNKNOWN, MMAL_ENCODING_UNKNOWN, 0, 0} |
152 | }; |
153 | |
154 | static MMAL_FOURCC_T container_to_mmal_encoding(VC_CONTAINER_FOURCC_T codec) |
155 | { |
156 | unsigned int i; |
157 | for(i = 0; encoding_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) |
158 | if(encoding_table[i].codec == codec) |
159 | break; |
160 | return encoding_table[i].encoding; |
161 | } |
162 | |
163 | static VC_CONTAINER_FOURCC_T mmal_to_container_encoding(uint32_t encoding) |
164 | { |
165 | unsigned int i; |
166 | for(i = 0; encoding_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) |
167 | if(encoding_table[i].encoding == encoding) |
168 | break; |
169 | return encoding_table[i].codec; |
170 | } |
171 | |
172 | static MMAL_FOURCC_T container_to_mmal_variant(VC_CONTAINER_FOURCC_T codec, |
173 | VC_CONTAINER_FOURCC_T codec_variant) |
174 | { |
175 | unsigned int i; |
176 | for(i = 0; encoding_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) |
177 | if(encoding_table[i].codec == codec && |
178 | encoding_table[i].codec_variant == codec_variant) |
179 | break; |
180 | return encoding_table[i].encoding_variant; |
181 | } |
182 | |
183 | static VC_CONTAINER_FOURCC_T mmal_to_container_variant(MMAL_FOURCC_T encoding, |
184 | MMAL_FOURCC_T encoding_variant) |
185 | { |
186 | unsigned int i; |
187 | for(i = 0; encoding_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) |
188 | if(encoding_table[i].encoding == encoding && |
189 | encoding_table[i].encoding_variant == encoding_variant) |
190 | break; |
191 | return encoding_table[i].codec_variant; |
192 | } |
193 | |
194 | /*****************************************************************************/ |
195 | static struct { |
196 | VC_CONTAINER_ES_TYPE_T container_type; |
197 | MMAL_ES_TYPE_T type; |
198 | } es_type_table[] = |
199 | { |
200 | {VC_CONTAINER_ES_TYPE_VIDEO, MMAL_ES_TYPE_VIDEO}, |
201 | {VC_CONTAINER_ES_TYPE_AUDIO, MMAL_ES_TYPE_AUDIO}, |
202 | {VC_CONTAINER_ES_TYPE_SUBPICTURE, MMAL_ES_TYPE_SUBPICTURE}, |
203 | {VC_CONTAINER_ES_TYPE_UNKNOWN, MMAL_ES_TYPE_UNKNOWN} |
204 | }; |
205 | |
206 | static MMAL_ES_TYPE_T container_to_mmal_es_type(VC_CONTAINER_ES_TYPE_T type) |
207 | { |
208 | unsigned int i; |
209 | for(i = 0; es_type_table[i].container_type != VC_CONTAINER_ES_TYPE_UNKNOWN; i++) |
210 | if(es_type_table[i].container_type == type) |
211 | break; |
212 | return es_type_table[i].type; |
213 | } |
214 | |
215 | static VC_CONTAINER_ES_TYPE_T mmal_to_container_es_type(MMAL_ES_TYPE_T type) |
216 | { |
217 | unsigned int i; |
218 | for(i = 0; es_type_table[i].container_type != VC_CONTAINER_ES_TYPE_UNKNOWN; i++) |
219 | if(es_type_table[i].type == type) |
220 | break; |
221 | return es_type_table[i].container_type; |
222 | } |
223 | |
224 | static MMAL_STATUS_T container_map_to_mmal_status(VC_CONTAINER_STATUS_T cstatus) |
225 | { |
226 | switch (cstatus) |
227 | { |
228 | case VC_CONTAINER_SUCCESS: return MMAL_SUCCESS; |
229 | case VC_CONTAINER_ERROR_CORRUPTED: return MMAL_ECORRUPT; |
230 | case VC_CONTAINER_ERROR_OUT_OF_MEMORY: return MMAL_ENOMEM; |
231 | case VC_CONTAINER_ERROR_OUT_OF_RESOURCES: return MMAL_ENOSPC; |
232 | case VC_CONTAINER_ERROR_NOT_READY: return MMAL_ENOTREADY; |
233 | case VC_CONTAINER_ERROR_NOT_FOUND: return MMAL_ENOENT; |
234 | case VC_CONTAINER_ERROR_URI_NOT_FOUND: return MMAL_ENOENT; |
235 | default: return MMAL_EINVAL; |
236 | } |
237 | } |
238 | |
239 | static MMAL_STATUS_T container_to_mmal_format(MMAL_ES_FORMAT_T *format, |
240 | VC_CONTAINER_ES_FORMAT_T *container_format) |
241 | { |
242 | format->type = container_to_mmal_es_type(container_format->es_type); |
243 | if(format->type == MMAL_ES_TYPE_UNKNOWN) |
244 | return MMAL_EINVAL; |
245 | |
246 | format->encoding = container_to_mmal_encoding(container_format->codec); |
247 | format->encoding_variant = container_to_mmal_variant(container_format->codec, container_format->codec_variant); |
248 | format->bitrate = container_format->bitrate; |
249 | format->flags = (container_format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED) ? |
250 | MMAL_ES_FORMAT_FLAG_FRAMED : 0; |
251 | memset(format->es, 0, sizeof(*format->es)); |
252 | |
253 | switch(format->type) |
254 | { |
255 | case MMAL_ES_TYPE_VIDEO: |
256 | format->es->video.width = container_format->type->video.width; |
257 | format->es->video.height = container_format->type->video.height; |
258 | format->es->video.crop.width = container_format->type->video.visible_width; |
259 | format->es->video.crop.height = container_format->type->video.visible_height; |
260 | format->es->video.frame_rate.num = container_format->type->video.frame_rate_num; |
261 | format->es->video.frame_rate.den = container_format->type->video.frame_rate_den; |
262 | format->es->video.par.num = container_format->type->video.par_num; |
263 | format->es->video.par.den = container_format->type->video.par_den; |
264 | break; |
265 | case MMAL_ES_TYPE_AUDIO: |
266 | format->es->audio.channels = container_format->type->audio.channels; |
267 | format->es->audio.sample_rate = container_format->type->audio.sample_rate; |
268 | format->es->audio.bits_per_sample = container_format->type->audio.bits_per_sample; |
269 | format->es->audio.block_align = container_format->type->audio.block_align; |
270 | break; |
271 | default: |
272 | LOG_ERROR("format es type not handled (%i)" , (int)format->type); |
273 | break; |
274 | } |
275 | |
276 | if(container_format->extradata_size) |
277 | { |
278 | MMAL_STATUS_T status = mmal_format_extradata_alloc(format, container_format->extradata_size); |
279 | if(status != MMAL_SUCCESS) |
280 | { |
281 | LOG_ERROR("couldn't allocate extradata" ); |
282 | return status; |
283 | } |
284 | format->extradata_size = container_format->extradata_size; |
285 | memcpy(format->extradata, container_format->extradata, format->extradata_size); |
286 | } |
287 | |
288 | return MMAL_SUCCESS; |
289 | } |
290 | |
291 | static MMAL_STATUS_T mmal_to_container_format(VC_CONTAINER_ES_FORMAT_T *container_format, |
292 | MMAL_ES_FORMAT_T *format) |
293 | { |
294 | container_format->es_type = mmal_to_container_es_type(format->type); |
295 | if(container_format->es_type == VC_CONTAINER_ES_TYPE_UNKNOWN) |
296 | return MMAL_EINVAL; |
297 | |
298 | container_format->codec = mmal_to_container_encoding(format->encoding); |
299 | container_format->codec_variant = mmal_to_container_variant(format->encoding, format->encoding_variant); |
300 | container_format->bitrate = format->bitrate; |
301 | container_format->flags = 0; |
302 | if(format->flags & MMAL_ES_FORMAT_FLAG_FRAMED) |
303 | container_format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; |
304 | memset(container_format->type, 0, sizeof(*container_format->type)); |
305 | |
306 | /* Auto-detect H264 AVC1 variant */ |
307 | if(format->encoding == MMAL_ENCODING_H264 && !format->encoding_variant && |
308 | format->extradata_size >= 5 && *format->extradata == 1) |
309 | container_format->codec_variant = VC_CONTAINER_VARIANT_H264_AVC1; |
310 | |
311 | switch(format->type) |
312 | { |
313 | case MMAL_ES_TYPE_VIDEO: |
314 | container_format->type->video.width = format->es->video.width; |
315 | container_format->type->video.height = format->es->video.height; |
316 | container_format->type->video.frame_rate_num = format->es->video.frame_rate.num; |
317 | container_format->type->video.frame_rate_den = format->es->video.frame_rate.den; |
318 | container_format->type->video.par_num = format->es->video.par.num; |
319 | container_format->type->video.par_den = format->es->video.par.den; |
320 | break; |
321 | case MMAL_ES_TYPE_AUDIO: |
322 | container_format->type->audio.channels = format->es->audio.channels; |
323 | container_format->type->audio.sample_rate = format->es->audio.sample_rate; |
324 | container_format->type->audio.bits_per_sample = format->es->audio.bits_per_sample; |
325 | container_format->type->audio.block_align = format->es->audio.block_align; |
326 | break; |
327 | default: |
328 | LOG_ERROR("format es type not handled (%i)" , (int)format->type); |
329 | break; |
330 | } |
331 | |
332 | container_format->extradata_size = format->extradata_size; |
333 | container_format->extradata = format->extradata; |
334 | |
335 | return MMAL_SUCCESS; |
336 | } |
337 | |
338 | /*****************************************************************************/ |
339 | static void reader_do_processing(MMAL_COMPONENT_T *component) |
340 | { |
341 | MMAL_COMPONENT_MODULE_T *module = component->priv->module; |
342 | MMAL_BUFFER_HEADER_T *buffer; |
343 | VC_CONTAINER_STATUS_T cstatus; |
344 | VC_CONTAINER_PACKET_T packet; |
345 | MMAL_STATUS_T status; |
346 | unsigned int i; |
347 | |
348 | memset(&packet, 0, sizeof(packet)); |
349 | |
350 | while(1) |
351 | { |
352 | cstatus = vc_container_read(module->container, &packet, VC_CONTAINER_READ_FLAG_INFO); |
353 | if(cstatus == VC_CONTAINER_ERROR_CONTINUE) |
354 | continue; |
355 | if(cstatus != VC_CONTAINER_SUCCESS) |
356 | { |
357 | LOG_DEBUG("READ EOF (%i)" , cstatus); |
358 | break; |
359 | } |
360 | |
361 | if (!module->packet_logged) |
362 | LOG_DEBUG("packet info: track %i, size %i/%i, pts %" PRId64"%s, dts %" PRId64"%s, flags %x%s" , |
363 | packet.track, packet.size, packet.frame_size, |
364 | packet.pts == VC_CONTAINER_TIME_UNKNOWN ? 0 : packet.pts, |
365 | packet.pts == VC_CONTAINER_TIME_UNKNOWN ? ":unknown" : "" , |
366 | packet.dts == VC_CONTAINER_TIME_UNKNOWN ? 0 : packet.dts, |
367 | packet.dts == VC_CONTAINER_TIME_UNKNOWN ? ":unknown" : "" , |
368 | packet.flags, (packet.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) ? " (keyframe)" : "" ); |
369 | |
370 | /* Find the port corresponding to that track */ |
371 | for(i = 0; i < module->ports; i++) |
372 | if(component->output[i]->priv->module->track == packet.track) |
373 | break; |
374 | if(i == module->ports) |
375 | { |
376 | vc_container_read(module->container, 0, VC_CONTAINER_READ_FLAG_SKIP); |
377 | continue; |
378 | } |
379 | |
380 | /* Get a buffer from this port */ |
381 | buffer = mmal_queue_get(component->output[i]->priv->module->queue); |
382 | if(!buffer) |
383 | { |
384 | module->packet_logged = 1; |
385 | break; /* Try again next time */ |
386 | } |
387 | module->packet_logged = 0; |
388 | |
389 | if(component->output[i]->priv->module->flush) |
390 | { |
391 | buffer->length = 0; |
392 | component->output[i]->priv->module->flush = MMAL_FALSE; |
393 | } |
394 | |
395 | mmal_buffer_header_mem_lock(buffer); |
396 | packet.data = buffer->data + buffer->length; |
397 | packet.buffer_size = buffer->alloc_size - buffer->length; |
398 | packet.size = 0; |
399 | cstatus = vc_container_read(module->container, &packet, 0); |
400 | mmal_buffer_header_mem_unlock(buffer); |
401 | if(cstatus != VC_CONTAINER_SUCCESS) |
402 | { |
403 | LOG_DEBUG("TEST read status: %i" , cstatus); |
404 | mmal_queue_put_back(component->output[i]->priv->module->queue, buffer); |
405 | break; |
406 | } |
407 | |
408 | if(!buffer->length) |
409 | { |
410 | buffer->pts = packet.pts == VC_CONTAINER_TIME_UNKNOWN ? MMAL_TIME_UNKNOWN : packet.pts; |
411 | buffer->dts = packet.dts == VC_CONTAINER_TIME_UNKNOWN ? MMAL_TIME_UNKNOWN : packet.dts; |
412 | buffer->flags = 0; |
413 | if(packet.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) |
414 | buffer->flags |= MMAL_BUFFER_HEADER_FLAG_KEYFRAME; |
415 | if(packet.flags & VC_CONTAINER_PACKET_FLAG_FRAME_START) |
416 | buffer->flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_START; |
417 | } |
418 | if(packet.flags & VC_CONTAINER_PACKET_FLAG_FRAME_END) |
419 | buffer->flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END; |
420 | #ifdef VC_CONTAINER_PACKET_FLAG_CONFIG |
421 | if(packet.flags & VC_CONTAINER_PACKET_FLAG_CONFIG) |
422 | buffer->flags |= MMAL_BUFFER_HEADER_FLAG_CONFIG; |
423 | #endif |
424 | |
425 | buffer->length += packet.size; |
426 | |
427 | if((component->output[i]->format->flags & MMAL_ES_FORMAT_FLAG_FRAMED) && |
428 | buffer->length != buffer->alloc_size && |
429 | !(buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END)) |
430 | { |
431 | mmal_queue_put_back(component->output[i]->priv->module->queue, buffer); |
432 | continue; |
433 | } |
434 | |
435 | /* Send buffer back */ |
436 | mmal_port_buffer_header_callback(component->output[i], buffer); |
437 | } |
438 | |
439 | if(cstatus == VC_CONTAINER_ERROR_EOS) |
440 | { |
441 | /* Send an empty EOS buffer for each port */ |
442 | for(i = 0; i < component->output_num; i++) |
443 | { |
444 | MMAL_PORT_T *port = component->output[i]; |
445 | if(!port->is_enabled) |
446 | continue; |
447 | if(port->priv->module->eos) |
448 | continue; |
449 | /* Get a buffer from this port */ |
450 | buffer = mmal_queue_get(port->priv->module->queue); |
451 | if(!buffer) |
452 | continue; /* Try again next time */ |
453 | buffer->length = 0; |
454 | buffer->pts = buffer->dts = MMAL_TIME_UNKNOWN; |
455 | buffer->flags = MMAL_BUFFER_HEADER_FLAG_EOS; |
456 | /* Send buffer back */ |
457 | port->priv->module->eos = 1; |
458 | mmal_port_buffer_header_callback(port, buffer); |
459 | } |
460 | } |
461 | else if(cstatus != VC_CONTAINER_SUCCESS && !module->error) |
462 | { |
463 | status = mmal_event_error_send(component, container_map_to_mmal_status(cstatus)); |
464 | if (status != MMAL_SUCCESS) |
465 | { |
466 | LOG_ERROR("unable to send an error event buffer (%i)" , (int)status); |
467 | return; |
468 | } |
469 | module->error = 1; |
470 | } |
471 | |
472 | return; |
473 | } |
474 | |
475 | /*****************************************************************************/ |
476 | static void writer_do_processing(MMAL_COMPONENT_T *component) |
477 | { |
478 | MMAL_COMPONENT_MODULE_T *module = component->priv->module; |
479 | VC_CONTAINER_STATUS_T cstatus; |
480 | MMAL_PORT_MODULE_T *port_module; |
481 | MMAL_PORT_T *port; |
482 | MMAL_STATUS_T status; |
483 | MMAL_BOOL_T eos; |
484 | int64_t timestamp, timestamp_current; |
485 | MMAL_BUFFER_HEADER_T *buffer; |
486 | VC_CONTAINER_PACKET_T packet; |
487 | unsigned int i, index; |
488 | |
489 | if(module->error) |
490 | return; |
491 | |
492 | /* Select the next port to read from based on earliest timestamp. Buffers without |
493 | * timestamps will end-up being prioritised. */ |
494 | for(i = 0, index = module->port_last_used, port = 0, timestamp = INT64_C(0); |
495 | i < component->input_num; i++, index++) |
496 | { |
497 | if(index == component->input_num) |
498 | index = 0; |
499 | |
500 | if(!component->input[index]->is_enabled) |
501 | continue; |
502 | |
503 | buffer = mmal_queue_get(component->input[index]->priv->module->queue); |
504 | if(!buffer) |
505 | continue; |
506 | |
507 | timestamp_current = buffer->dts; |
508 | if (timestamp_current == MMAL_TIME_UNKNOWN) |
509 | timestamp_current = buffer->pts; |
510 | if(!port) |
511 | timestamp = timestamp_current; |
512 | |
513 | if(timestamp_current <= timestamp) |
514 | { |
515 | port = component->input[index]; |
516 | timestamp = timestamp_current; |
517 | module->port_last_used = index; |
518 | } |
519 | mmal_queue_put_back(component->input[index]->priv->module->queue, buffer); |
520 | } |
521 | |
522 | /* If a port is currently writing a frame then we override the decision to avoid |
523 | * splitting frames */ |
524 | if(module->port_writing_frame && module->port_writing_frame - 1 < component->input_num && |
525 | component->input[module->port_writing_frame-1]->is_enabled) |
526 | port = component->input[module->port_writing_frame-1]; |
527 | |
528 | if(!port) |
529 | return; /* nothing to write */ |
530 | |
531 | port_module = port->priv->module; |
532 | |
533 | /* Get a buffer from this port */ |
534 | buffer = mmal_queue_get(port_module->queue); |
535 | if(!buffer) |
536 | return; /* nothing to write */ |
537 | |
538 | mmal_buffer_header_mem_lock(buffer); |
539 | memset(&packet, 0, sizeof(packet)); |
540 | packet.track = port_module->track; |
541 | packet.size = buffer->length; |
542 | packet.data = buffer->data + buffer->offset; |
543 | packet.pts = buffer->pts == MMAL_TIME_UNKNOWN ? VC_CONTAINER_TIME_UNKNOWN : buffer->pts; |
544 | packet.dts = buffer->dts == MMAL_TIME_UNKNOWN ? VC_CONTAINER_TIME_UNKNOWN : buffer->dts; |
545 | if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME) |
546 | packet.flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; |
547 | if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_START) |
548 | packet.flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; |
549 | if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) |
550 | packet.flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; |
551 | eos = buffer->flags & MMAL_BUFFER_HEADER_FLAG_EOS; |
552 | |
553 | if ((packet.flags & VC_CONTAINER_PACKET_FLAG_FRAME) == VC_CONTAINER_PACKET_FLAG_FRAME) |
554 | packet.frame_size = packet.size; |
555 | else |
556 | packet.frame_size = 0; |
557 | |
558 | module->port_writing_frame = port->index + 1; |
559 | if((buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) || |
560 | !(port->format->flags & MMAL_ES_FORMAT_FLAG_FRAMED)) |
561 | module->port_writing_frame = 0; |
562 | |
563 | LOG_DEBUG("packet info: track %i, size %i/%i, pts %" PRId64", flags %x%s" , |
564 | packet.track, packet.size, packet.frame_size, packet.pts, |
565 | packet.flags, (packet.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) ? " (keyframe)" : "" ); |
566 | |
567 | cstatus = vc_container_write(module->container, &packet); |
568 | mmal_buffer_header_mem_unlock(buffer); |
569 | |
570 | /* Send buffer back */ |
571 | buffer->length = 0; |
572 | mmal_port_buffer_header_callback(port, buffer); |
573 | |
574 | /* Check for errors */ |
575 | if(cstatus != VC_CONTAINER_SUCCESS) |
576 | { |
577 | LOG_ERROR("write failed (%i)" , (int)cstatus); |
578 | status = mmal_event_error_send(component, container_map_to_mmal_status(cstatus)); |
579 | if (status != MMAL_SUCCESS) |
580 | { |
581 | LOG_ERROR("unable to send an error event buffer (%i)" , (int)status); |
582 | return; |
583 | } |
584 | module->error = 1; |
585 | return; |
586 | } |
587 | |
588 | /* Generate EOS events */ |
589 | if(eos) |
590 | { |
591 | MMAL_EVENT_END_OF_STREAM_T *event; |
592 | status = mmal_port_event_get(component->control, &buffer, MMAL_EVENT_EOS); |
593 | if (status != MMAL_SUCCESS) |
594 | { |
595 | LOG_ERROR("unable to get an event buffer" ); |
596 | return; |
597 | } |
598 | |
599 | buffer->length = sizeof(*event); |
600 | event = (MMAL_EVENT_END_OF_STREAM_T *)buffer->data; |
601 | event->port_type = port->type; |
602 | event->port_index = port->index; |
603 | mmal_port_event_send(component->control, buffer); |
604 | } |
605 | } |
606 | |
607 | /** Destroy a previously created component */ |
608 | static MMAL_STATUS_T container_component_destroy(MMAL_COMPONENT_T *component) |
609 | { |
610 | MMAL_COMPONENT_MODULE_T *module = component->priv->module; |
611 | unsigned int i; |
612 | |
613 | if(module->container) |
614 | vc_container_close(module->container); |
615 | |
616 | for(i = 0; i < component->input_num; i++) |
617 | { |
618 | if(component->input[i]->priv->module->queue) |
619 | mmal_queue_destroy(component->input[i]->priv->module->queue); |
620 | if(component->input[i]->priv->module->format) |
621 | vc_container_format_delete(component->input[i]->priv->module->format); |
622 | } |
623 | if(component->input_num) |
624 | mmal_ports_free(component->input, component->input_num); |
625 | |
626 | for(i = 0; i < component->output_num; i++) |
627 | if(component->output[i]->priv->module->queue) |
628 | mmal_queue_destroy(component->output[i]->priv->module->queue); |
629 | if(component->output_num) |
630 | mmal_ports_free(component->output, component->output_num); |
631 | |
632 | vcos_free(module); |
633 | return MMAL_SUCCESS; |
634 | } |
635 | |
636 | /** Enable processing on a port */ |
637 | static MMAL_STATUS_T container_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb) |
638 | { |
639 | MMAL_COMPONENT_T *component = port->component; |
640 | MMAL_COMPONENT_MODULE_T *module = component->priv->module; |
641 | MMAL_PORT_MODULE_T *port_module = port->priv->module; |
642 | MMAL_PARAM_UNUSED(cb); |
643 | |
644 | if(!module->container) |
645 | return MMAL_EINVAL; |
646 | |
647 | if(module->writer) |
648 | { |
649 | VC_CONTAINER_STATUS_T cstatus; |
650 | port_module->track = module->container->tracks_num; |
651 | cstatus = vc_container_control(module->container, VC_CONTAINER_CONTROL_TRACK_ADD, |
652 | port_module->format); |
653 | if(cstatus != VC_CONTAINER_SUCCESS) |
654 | { |
655 | LOG_ERROR("error adding track %4.4s (%i)" , (char *)&port->format->encoding, (int)cstatus); |
656 | return container_map_to_mmal_status(cstatus); |
657 | } |
658 | } |
659 | |
660 | if(port_module->track >= module->container->tracks_num) |
661 | { |
662 | LOG_ERROR("error 1 adding track %4.4s (%i/%i)" , (char *)&port->format->encoding, port_module->track, module->container->tracks_num); |
663 | return MMAL_EINVAL; |
664 | } |
665 | module->container->tracks[port_module->track]->is_enabled = 1; |
666 | return MMAL_SUCCESS; |
667 | } |
668 | |
669 | /** Flush a port */ |
670 | static MMAL_STATUS_T container_port_flush(MMAL_PORT_T *port) |
671 | { |
672 | MMAL_PORT_MODULE_T *port_module = port->priv->module; |
673 | MMAL_BUFFER_HEADER_T *buffer; |
674 | |
675 | /* Flush buffers that our component is holding on to. |
676 | * If the reading thread is holding onto a buffer it will send it back ASAP as well |
677 | * so no need to care about that. */ |
678 | buffer = mmal_queue_get(port_module->queue); |
679 | while(buffer) |
680 | { |
681 | buffer->length = 0; |
682 | mmal_port_buffer_header_callback(port, buffer); |
683 | buffer = mmal_queue_get(port_module->queue); |
684 | } |
685 | |
686 | return MMAL_SUCCESS; |
687 | } |
688 | |
689 | /** Disable processing on a port */ |
690 | static MMAL_STATUS_T container_port_disable(MMAL_PORT_T *port) |
691 | { |
692 | MMAL_COMPONENT_T *component = port->component; |
693 | MMAL_COMPONENT_MODULE_T *module = component->priv->module; |
694 | unsigned int track = port->priv->module->track; |
695 | MMAL_STATUS_T status; |
696 | |
697 | if(!module->container || track >= module->container->tracks_num) |
698 | return MMAL_EINVAL; |
699 | |
700 | /* Actions are prevented from running at that point so a flush |
701 | * will return all buffers. */ |
702 | status = container_port_flush(port); |
703 | if(status != MMAL_SUCCESS) |
704 | return status; |
705 | |
706 | module->container->tracks[track]->is_enabled = 0; |
707 | return MMAL_SUCCESS; |
708 | } |
709 | |
710 | /** Send a buffer header to a port */ |
711 | static MMAL_STATUS_T container_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
712 | { |
713 | mmal_queue_put(port->priv->module->queue, buffer); |
714 | mmal_component_action_trigger(port->component); |
715 | return MMAL_SUCCESS; |
716 | } |
717 | |
718 | /** Set format on a port */ |
719 | static MMAL_STATUS_T container_port_set_format(MMAL_PORT_T *port) |
720 | { |
721 | MMAL_COMPONENT_T *component = port->component; |
722 | MMAL_COMPONENT_MODULE_T *module = component->priv->module; |
723 | MMAL_STATUS_T status; |
724 | |
725 | if(!module->writer) |
726 | return MMAL_EINVAL; |
727 | |
728 | /* Set format of the track */ |
729 | status = mmal_to_container_format(port->priv->module->format, port->format); |
730 | if (status != MMAL_SUCCESS) |
731 | return status; |
732 | |
733 | port->buffer_num_min = READER_MIN_BUFFER_NUM; |
734 | port->buffer_num_recommended = READER_RECOMMENDED_BUFFER_NUM; |
735 | port->buffer_size_min = READER_MIN_BUFFER_SIZE; |
736 | port->buffer_size_recommended = READER_RECOMMENDED_BUFFER_SIZE; |
737 | return MMAL_SUCCESS; |
738 | } |
739 | |
740 | static MMAL_STATUS_T reader_container_open(MMAL_COMPONENT_T *component, const char *uri) |
741 | { |
742 | MMAL_COMPONENT_MODULE_T *module = component->priv->module; |
743 | VC_CONTAINER_STATUS_T cstatus; |
744 | VC_CONTAINER_T *container; |
745 | unsigned int i, port, track; |
746 | |
747 | /* Open container */ |
748 | module->container = container = |
749 | vc_container_open_reader(uri, &cstatus, 0, 0); |
750 | if(!container) |
751 | { |
752 | LOG_ERROR("error opening file %s (%i)" , uri, cstatus); |
753 | return container_map_to_mmal_status(cstatus); |
754 | } |
755 | |
756 | /* Disable all tracks */ |
757 | for(track = 0; track < container->tracks_num; track++) |
758 | container->tracks[track]->is_enabled = 0; |
759 | |
760 | /* Fill-in the ports */ |
761 | for(i = 0, port = 0; i < component->output_num; i++) |
762 | { |
763 | VC_CONTAINER_ES_TYPE_T type = VC_CONTAINER_ES_TYPE_VIDEO; |
764 | if(i == 1) type = VC_CONTAINER_ES_TYPE_AUDIO; |
765 | if(i == 2) type = VC_CONTAINER_ES_TYPE_SUBPICTURE; |
766 | |
767 | /* Look for the first track with the specified type */ |
768 | for(track = 0; track < container->tracks_num; track++) |
769 | if(container->tracks[track]->format->es_type == type) |
770 | break; |
771 | if(track == container->tracks_num) |
772 | continue; |
773 | |
774 | if(container_to_mmal_encoding(container->tracks[track]->format->codec) == MMAL_ENCODING_UNKNOWN) |
775 | continue; |
776 | |
777 | /* Set format of the port */ |
778 | if(container_to_mmal_format(component->output[port]->format, |
779 | container->tracks[track]->format) != MMAL_SUCCESS) |
780 | continue; |
781 | |
782 | component->output[port]->buffer_num_min = READER_MIN_BUFFER_NUM; |
783 | component->output[port]->buffer_num_recommended = READER_RECOMMENDED_BUFFER_NUM; |
784 | component->output[port]->buffer_size_min = READER_MIN_BUFFER_SIZE; |
785 | component->output[port]->buffer_size_recommended = READER_RECOMMENDED_BUFFER_SIZE; |
786 | component->output[port]->priv->module->track = track; |
787 | |
788 | /* We're done with this port */ |
789 | port++; |
790 | } |
791 | module->ports = port; |
792 | |
793 | /* Reset the left over ports */ |
794 | for(i = port; i < component->output_num; i++) |
795 | { |
796 | component->output[i]->format->type = MMAL_ES_TYPE_UNKNOWN; |
797 | component->output[i]->format->encoding = MMAL_ENCODING_UNKNOWN; |
798 | } |
799 | |
800 | return MMAL_SUCCESS; |
801 | } |
802 | |
803 | static MMAL_STATUS_T reader_container_seek(MMAL_COMPONENT_T *component, const MMAL_PARAMETER_SEEK_T *seek) |
804 | { |
805 | MMAL_COMPONENT_MODULE_T *module = component->priv->module; |
806 | VC_CONTAINER_SEEK_FLAGS_T flags = 0; |
807 | int64_t offset = seek->offset; |
808 | VC_CONTAINER_STATUS_T cstatus; |
809 | unsigned int i; |
810 | |
811 | if(seek->flags & MMAL_PARAM_SEEK_FLAG_PRECISE) |
812 | flags |= VC_CONTAINER_SEEK_FLAG_PRECISE; |
813 | if(seek->flags & MMAL_PARAM_SEEK_FLAG_FORWARD) |
814 | flags |= VC_CONTAINER_SEEK_FLAG_FORWARD; |
815 | |
816 | mmal_component_action_lock(component); |
817 | for(i = 0; i < component->output_num; i++) |
818 | { |
819 | component->output[i]->priv->module->eos = MMAL_FALSE; |
820 | component->output[i]->priv->module->flush = MMAL_TRUE; |
821 | } |
822 | cstatus = vc_container_seek( module->container, &offset, VC_CONTAINER_SEEK_MODE_TIME, flags); |
823 | mmal_component_action_unlock(component); |
824 | return container_map_to_mmal_status(cstatus); |
825 | } |
826 | |
827 | static MMAL_STATUS_T reader_parameter_set(MMAL_PORT_T *port, const MMAL_PARAMETER_HEADER_T *param) |
828 | { |
829 | MMAL_COMPONENT_T *component = port->component; |
830 | MMAL_COMPONENT_MODULE_T *module = component->priv->module; |
831 | |
832 | switch(param->id) |
833 | { |
834 | case MMAL_PARAMETER_URI: |
835 | if(module->container) |
836 | return MMAL_EINVAL; |
837 | |
838 | memset(module->uri, 0, sizeof(module->uri)); |
839 | strncpy(module->uri, ((const MMAL_PARAMETER_STRING_T *)param)->str, sizeof(module->uri)-1 ); |
840 | return reader_container_open(component, module->uri); |
841 | |
842 | case MMAL_PARAMETER_SEEK: |
843 | if(!module->container || param->size < sizeof(MMAL_PARAMETER_SEEK_T)) |
844 | return MMAL_EINVAL; |
845 | |
846 | return reader_container_seek(component, (const MMAL_PARAMETER_SEEK_T *)param); |
847 | |
848 | default: |
849 | return MMAL_ENOSYS; |
850 | } |
851 | |
852 | return MMAL_SUCCESS; |
853 | } |
854 | |
855 | /** Create an instance of a component */ |
856 | static MMAL_STATUS_T mmal_component_create_reader(const char *name, MMAL_COMPONENT_T *component) |
857 | { |
858 | MMAL_COMPONENT_MODULE_T *module; |
859 | unsigned int outputs_num, i; |
860 | MMAL_STATUS_T status = MMAL_ENOMEM; |
861 | MMAL_PARAM_UNUSED(name); |
862 | |
863 | /* Allocate the context for our module */ |
864 | component->priv->module = module = vcos_malloc(sizeof(*module), "mmal module" ); |
865 | if (!module) |
866 | return MMAL_ENOMEM; |
867 | memset(module, 0, sizeof(*module)); |
868 | |
869 | component->priv->pf_destroy = container_component_destroy; |
870 | |
871 | /* Create 3 tracks for now (audio/video/subpicture). |
872 | * FIXME: ideally we should create 1 track per elementary stream. */ |
873 | outputs_num = 3; |
874 | |
875 | /* Now the component on reader has been created, we can allocate |
876 | * the ports for this component */ |
877 | component->output = mmal_ports_alloc(component, outputs_num, MMAL_PORT_TYPE_OUTPUT, |
878 | sizeof(MMAL_PORT_MODULE_T)); |
879 | if(!component->output) |
880 | goto error; |
881 | component->output_num = outputs_num; |
882 | |
883 | for(i = 0; i < outputs_num; i++) |
884 | { |
885 | component->output[i]->priv->pf_enable = container_port_enable; |
886 | component->output[i]->priv->pf_disable = container_port_disable; |
887 | component->output[i]->priv->pf_flush = container_port_flush; |
888 | component->output[i]->priv->pf_send = container_port_send; |
889 | component->output[i]->priv->module->queue = mmal_queue_create(); |
890 | if(!component->output[i]->priv->module->queue) |
891 | goto error; |
892 | } |
893 | component->control->priv->pf_parameter_set = reader_parameter_set; |
894 | |
895 | status = mmal_component_action_register(component, reader_do_processing); |
896 | if (status != MMAL_SUCCESS) |
897 | goto error; |
898 | |
899 | return MMAL_SUCCESS; |
900 | |
901 | error: |
902 | container_component_destroy(component); |
903 | return status; |
904 | } |
905 | |
906 | static MMAL_STATUS_T writer_parameter_set(MMAL_PORT_T *port, const MMAL_PARAMETER_HEADER_T *param) |
907 | { |
908 | MMAL_COMPONENT_T *component = port->component; |
909 | MMAL_COMPONENT_MODULE_T *module = component->priv->module; |
910 | VC_CONTAINER_STATUS_T cstatus; |
911 | |
912 | switch(param->id) |
913 | { |
914 | case MMAL_PARAMETER_URI: |
915 | if(module->container) |
916 | return MMAL_EINVAL; |
917 | |
918 | memset(module->uri, 0, sizeof(module->uri)); |
919 | strncpy(module->uri, ((const MMAL_PARAMETER_URI_T *)param)->uri, sizeof(module->uri)-1 ); |
920 | |
921 | /* Open container */ |
922 | module->container = vc_container_open_writer(module->uri, &cstatus, 0, 0); |
923 | if(!module->container) |
924 | { |
925 | LOG_ERROR("error opening file %s (%i)" , module->uri, cstatus); |
926 | return container_map_to_mmal_status(cstatus); |
927 | } |
928 | return MMAL_SUCCESS; |
929 | |
930 | default: |
931 | return MMAL_ENOSYS; |
932 | } |
933 | |
934 | return MMAL_SUCCESS; |
935 | } |
936 | |
937 | /** Create an instance of a component */ |
938 | static MMAL_STATUS_T mmal_component_create_writer(const char *name, MMAL_COMPONENT_T *component) |
939 | { |
940 | MMAL_COMPONENT_MODULE_T *module; |
941 | MMAL_STATUS_T status = MMAL_ENOMEM; |
942 | unsigned int i; |
943 | MMAL_PARAM_UNUSED(name); |
944 | |
945 | /* Allocate the context for our module */ |
946 | component->priv->module = module = vcos_malloc(sizeof(*module), "mmal module" ); |
947 | if (!module) |
948 | return MMAL_ENOMEM; |
949 | memset(module, 0, sizeof(*module)); |
950 | module->writer = 1; |
951 | |
952 | component->priv->pf_destroy = container_component_destroy; |
953 | |
954 | /* Now the component on reader has been created, we can allocate |
955 | * the ports for this component */ |
956 | component->input = mmal_ports_alloc(component, WRITER_PORTS_NUM, MMAL_PORT_TYPE_INPUT, |
957 | sizeof(MMAL_PORT_MODULE_T)); |
958 | if(!component->input) |
959 | goto error; |
960 | component->input_num = WRITER_PORTS_NUM; |
961 | |
962 | for(i = 0; i < component->input_num; i++) |
963 | { |
964 | component->input[i]->priv->pf_enable = container_port_enable; |
965 | component->input[i]->priv->pf_disable = container_port_disable; |
966 | component->input[i]->priv->pf_flush = container_port_flush; |
967 | component->input[i]->priv->pf_send = container_port_send; |
968 | component->input[i]->priv->pf_set_format = container_port_set_format; |
969 | |
970 | component->input[i]->priv->module->queue = mmal_queue_create(); |
971 | if(!component->input[i]->priv->module->queue) |
972 | goto error; |
973 | component->input[i]->priv->module->format = vc_container_format_create(0); |
974 | if(!component->input[i]->priv->module->format) |
975 | goto error; |
976 | } |
977 | component->control->priv->pf_parameter_set = writer_parameter_set; |
978 | |
979 | status = mmal_component_action_register(component, writer_do_processing); |
980 | if (status != MMAL_SUCCESS) |
981 | goto error; |
982 | |
983 | return MMAL_SUCCESS; |
984 | |
985 | error: |
986 | container_component_destroy(component); |
987 | return status; |
988 | } |
989 | |
990 | MMAL_CONSTRUCTOR(mmal_register_component_container_reader); |
991 | void mmal_register_component_container_reader(void) |
992 | { |
993 | mmal_component_supplier_register("container_reader" , mmal_component_create_reader); |
994 | } |
995 | |
996 | MMAL_CONSTRUCTOR(mmal_register_component_container_writer); |
997 | void mmal_register_component_container_writer(void) |
998 | { |
999 | mmal_component_supplier_register("container_writer" , mmal_component_create_writer); |
1000 | } |
1001 | |