| 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 | |