| 1 | /* |
| 2 | Copyright (c) 2012, Broadcom Europe Ltd |
| 3 | All rights reserved. |
| 4 | |
| 5 | Redistribution and use in source and binary forms, with or without |
| 6 | modification, are permitted provided that the following conditions are met: |
| 7 | * Redistributions of source code must retain the above copyright |
| 8 | notice, this list of conditions and the following disclaimer. |
| 9 | * Redistributions in binary form must reproduce the above copyright |
| 10 | notice, this list of conditions and the following disclaimer in the |
| 11 | documentation and/or other materials provided with the distribution. |
| 12 | * Neither the name of the copyright holder nor the |
| 13 | names of its contributors may be used to endorse or promote products |
| 14 | derived from this software without specific prior written permission. |
| 15 | |
| 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
| 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 26 | */ |
| 27 | #include <stdlib.h> |
| 28 | #include <string.h> |
| 29 | |
| 30 | //#define ENABLE_CONTAINERS_LOG_FORMAT |
| 31 | #include "containers/core/containers_private.h" |
| 32 | #include "containers/core/containers_io_helpers.h" |
| 33 | #include "containers/core/containers_utils.h" |
| 34 | #include "containers/core/containers_writer_utils.h" |
| 35 | #include "containers/core/containers_logging.h" |
| 36 | #undef CONTAINER_HELPER_LOG_INDENT |
| 37 | #define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->object_level |
| 38 | |
| 39 | VC_CONTAINER_STATUS_T asf_writer_open( VC_CONTAINER_T *p_ctx ); |
| 40 | |
| 41 | /****************************************************************************** |
| 42 | Defines. |
| 43 | ******************************************************************************/ |
| 44 | #define ASF_TRACKS_MAX 16 |
| 45 | #define (16+8) |
| 46 | |
| 47 | /****************************************************************************** |
| 48 | Type definitions. |
| 49 | ******************************************************************************/ |
| 50 | typedef enum { |
| 51 | ASF_OBJECT_TYPE_UNKNOWN = 0, |
| 52 | , |
| 53 | ASF_OBJECT_TYPE_FILE_PROPS, |
| 54 | ASF_OBJECT_TYPE_STREAM_PROPS, |
| 55 | ASF_OBJECT_TYPE_EXT_STREAM_PROPS, |
| 56 | ASF_OBJECT_TYPE_DATA, |
| 57 | ASF_OBJECT_TYPE_SIMPLE_INDEX, |
| 58 | ASF_OBJECT_TYPE_INDEX, |
| 59 | , |
| 60 | , |
| 61 | ASF_OBJECT_TYPE_CODEC_LIST, |
| 62 | ASF_OBJECT_TYPE_CONTENT_DESCRIPTION, |
| 63 | ASF_OBJECT_TYPE_EXT_CONTENT_DESCRIPTION, |
| 64 | ASF_OBJECT_TYPE_STREAM_BITRATE_PROPS, |
| 65 | ASF_OBJECT_TYPE_LANGUAGE_LIST, |
| 66 | ASF_OBJECT_TYPE_METADATA, |
| 67 | ASF_OBJECT_TYPE_PADDING, |
| 68 | } ASF_OBJECT_TYPE_T; |
| 69 | |
| 70 | typedef struct VC_CONTAINER_TRACK_MODULE_T |
| 71 | { |
| 72 | unsigned int stream_id; |
| 73 | uint64_t time_offset; |
| 74 | bool b_valid; |
| 75 | |
| 76 | uint64_t index_offset; |
| 77 | uint32_t num_index_entries; |
| 78 | int64_t index_time_interval; |
| 79 | } VC_CONTAINER_TRACK_MODULE_T; |
| 80 | |
| 81 | typedef struct VC_CONTAINER_MODULE_T |
| 82 | { |
| 83 | int object_level; |
| 84 | uint32_t packet_size; |
| 85 | |
| 86 | VC_CONTAINER_TRACK_T *tracks[ASF_TRACKS_MAX]; |
| 87 | |
| 88 | VC_CONTAINER_WRITER_EXTRAIO_T null; |
| 89 | bool ; |
| 90 | |
| 91 | unsigned int current_track; |
| 92 | |
| 93 | } VC_CONTAINER_MODULE_T; |
| 94 | |
| 95 | /****************************************************************************** |
| 96 | Static functions within this file. |
| 97 | ******************************************************************************/ |
| 98 | static VC_CONTAINER_STATUS_T asf_write_object( VC_CONTAINER_T *p_ctx, ASF_OBJECT_TYPE_T object_type ); |
| 99 | static VC_CONTAINER_STATUS_T asf_write_object_header( VC_CONTAINER_T *p_ctx ); |
| 100 | static VC_CONTAINER_STATUS_T asf_write_object_header_ext( VC_CONTAINER_T *p_ctx ); |
| 101 | static VC_CONTAINER_STATUS_T asf_write_object_header_ext_internal( VC_CONTAINER_T *p_ctx ); |
| 102 | static VC_CONTAINER_STATUS_T asf_write_object_file_properties( VC_CONTAINER_T *p_ctx ); |
| 103 | static VC_CONTAINER_STATUS_T asf_write_object_stream_properties( VC_CONTAINER_T *p_ctx ); |
| 104 | static VC_CONTAINER_STATUS_T asf_write_object_ext_stream_properties( VC_CONTAINER_T *p_ctx ); |
| 105 | static VC_CONTAINER_STATUS_T asf_write_object_simple_index( VC_CONTAINER_T *p_ctx ); |
| 106 | static VC_CONTAINER_STATUS_T asf_write_object_index( VC_CONTAINER_T *p_ctx ); |
| 107 | static VC_CONTAINER_STATUS_T asf_write_object_data( VC_CONTAINER_T *p_ctx ); |
| 108 | #if 0 |
| 109 | static VC_CONTAINER_STATUS_T asf_write_object_codec_list( VC_CONTAINER_T *p_ctx ); |
| 110 | static VC_CONTAINER_STATUS_T asf_write_object_content_description( VC_CONTAINER_T *p_ctx ); |
| 111 | static VC_CONTAINER_STATUS_T asf_write_object_stream_bitrate_props( VC_CONTAINER_T *p_ctx ); |
| 112 | #endif |
| 113 | |
| 114 | static const GUID_T = {0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; |
| 115 | static const GUID_T asf_guid_file_props = {0x8CABDCA1, 0xA947, 0x11CF, {0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; |
| 116 | static const GUID_T asf_guid_stream_props = {0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; |
| 117 | static const GUID_T asf_guid_ext_stream_props = {0x14E6A5CB, 0xC672, 0x4332, {0x83, 0x99, 0xA9, 0x69, 0x52, 0x06, 0x5B, 0x5A}}; |
| 118 | static const GUID_T asf_guid_data = {0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; |
| 119 | static const GUID_T asf_guid_simple_index = {0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}}; |
| 120 | static const GUID_T asf_guid_index = {0xD6E229D3, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}}; |
| 121 | static const GUID_T = {0x5FBF03B5, 0xA92E, 0x11CF, {0x8E, 0xE3, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; |
| 122 | static const GUID_T asf_guid_codec_list = {0x86D15240, 0x311D, 0x11D0, {0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}}; |
| 123 | static const GUID_T asf_guid_content_description = {0x75B22633, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; |
| 124 | static const GUID_T asf_guid_ext_content_description = {0xD2D0A440, 0xE307, 0x11D2, {0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50}}; |
| 125 | static const GUID_T asf_guid_stream_bitrate_props = {0x7BF875CE, 0x468D, 0x11D1, {0x8D, 0x82, 0x00, 0x60, 0x97, 0xC9, 0xA2, 0xB2}}; |
| 126 | static const GUID_T asf_guid_language_list = {0x7C4346A9, 0xEFE0, 0x4BFC, {0xB2, 0x29, 0x39, 0x3E, 0xDE, 0x41, 0x5C, 0x85}}; |
| 127 | static const GUID_T asf_guid_metadata = {0xC5F8CBEA, 0x5BAF, 0x4877, {0x84, 0x67, 0xAA, 0x8C, 0x44, 0xFA, 0x4C, 0xCA}}; |
| 128 | static const GUID_T asf_guid_padding = {0x1806D474, 0xCADF, 0x4509, {0xA4, 0xBA, 0x9A, 0xAB, 0xCB, 0x96, 0xAA, 0xE8}}; |
| 129 | |
| 130 | static const GUID_T asf_guid_stream_type_video = {0xBC19EFC0, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; |
| 131 | static const GUID_T asf_guid_stream_type_audio = {0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; |
| 132 | static const GUID_T asf_guid_error_correction = {0x20FB5700, 0x5B55, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; |
| 133 | |
| 134 | static struct { |
| 135 | const ASF_OBJECT_TYPE_T type; |
| 136 | const GUID_T *guid; |
| 137 | const char *psz_name; |
| 138 | VC_CONTAINER_STATUS_T (*pf_func)( VC_CONTAINER_T * ); |
| 139 | |
| 140 | } asf_object_list[] = |
| 141 | { |
| 142 | {ASF_OBJECT_TYPE_HEADER, &asf_guid_header, "header" , asf_write_object_header}, |
| 143 | {ASF_OBJECT_TYPE_FILE_PROPS, &asf_guid_file_props, "file properties" , asf_write_object_file_properties}, |
| 144 | {ASF_OBJECT_TYPE_STREAM_PROPS, &asf_guid_stream_props, "stream properties" , asf_write_object_stream_properties}, |
| 145 | {ASF_OBJECT_TYPE_EXT_STREAM_PROPS, &asf_guid_ext_stream_props, "extended stream properties" , asf_write_object_ext_stream_properties}, |
| 146 | {ASF_OBJECT_TYPE_DATA, &asf_guid_data, "data" , asf_write_object_data}, |
| 147 | {ASF_OBJECT_TYPE_SIMPLE_INDEX, &asf_guid_simple_index, "simple index" , asf_write_object_simple_index}, |
| 148 | {ASF_OBJECT_TYPE_INDEX, &asf_guid_index, "index" , asf_write_object_index}, |
| 149 | {ASF_OBJECT_TYPE_HEADER_EXT, &asf_guid_header_ext, "header extension" , asf_write_object_header_ext}, |
| 150 | {ASF_OBJECT_TYPE_HEADER_EXT_INTERNAL, &asf_guid_header_ext, "header extension" , asf_write_object_header_ext_internal}, |
| 151 | #if 0 |
| 152 | {ASF_OBJECT_TYPE_CODEC_LIST, &asf_guid_codec_list, "codec list" , asf_write_object_codec_list}, |
| 153 | {ASF_OBJECT_TYPE_CONTENT_DESCRIPTION, &asf_guid_content_description, "content description" , asf_write_object_content_description}, |
| 154 | {ASF_OBJECT_TYPE_EXT_CONTENT_DESCRIPTION, &asf_guid_ext_content_description, "extended content description" , 0}, |
| 155 | {ASF_OBJECT_TYPE_STREAM_BITRATE_PROPS, &asf_guid_stream_bitrate_props, "stream bitrate properties" , asf_write_object_stream_bitrate_props}, |
| 156 | #endif |
| 157 | {ASF_OBJECT_TYPE_UNKNOWN, 0, "unknown" , 0} |
| 158 | }; |
| 159 | |
| 160 | static GUID_T guid_null; |
| 161 | |
| 162 | /*****************************************************************************/ |
| 163 | static VC_CONTAINER_STATUS_T asf_write_object( VC_CONTAINER_T *p_ctx, ASF_OBJECT_TYPE_T type ) |
| 164 | { |
| 165 | VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
| 166 | VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
| 167 | int64_t object_size = 0; |
| 168 | unsigned int i; |
| 169 | |
| 170 | /* Find out which object we want to write */ |
| 171 | for( i = 0; asf_object_list[i].type && asf_object_list[i].type != type; i++ ); |
| 172 | |
| 173 | /* Check we found the requested type */ |
| 174 | if(!asf_object_list[i].type) |
| 175 | { |
| 176 | vc_container_assert(0); |
| 177 | return VC_CONTAINER_ERROR_CORRUPTED; |
| 178 | } |
| 179 | |
| 180 | /* We need to find out the size of the object we're going to write. |
| 181 | * Because we want to be streamable, we can't just come back later to update the size field. |
| 182 | * The easiest way to find out the size of the data we're going to write is to write a dummy |
| 183 | * version of it and get the size from that. It is a bit wasteful but is so much easier and |
| 184 | * shouldn't really impact performance as there's no actual i/o involved. */ |
| 185 | if(!vc_container_writer_extraio_enable(p_ctx, &module->null)) |
| 186 | { |
| 187 | asf_object_list[i].pf_func(p_ctx); |
| 188 | object_size = STREAM_POSITION(p_ctx); |
| 189 | } |
| 190 | vc_container_writer_extraio_disable(p_ctx, &module->null); |
| 191 | |
| 192 | /* Special case for header extension internal function */ |
| 193 | if(type == ASF_OBJECT_TYPE_HEADER_EXT_INTERNAL) |
| 194 | { |
| 195 | WRITE_U32(p_ctx, object_size, "Header Extension Data Size" ); |
| 196 | /* Call the object specific writing function */ |
| 197 | status = asf_object_list[i].pf_func(p_ctx); |
| 198 | return status; |
| 199 | } |
| 200 | |
| 201 | /* Write the object header */ |
| 202 | |
| 203 | if(WRITE_GUID(p_ctx, asf_object_list[i].guid, "Object ID" ) != sizeof(GUID_T)) |
| 204 | return VC_CONTAINER_ERROR_EOS; |
| 205 | |
| 206 | LOG_FORMAT(p_ctx, "Object Name: %s" , asf_object_list[i].psz_name); |
| 207 | |
| 208 | WRITE_U64(p_ctx, object_size + ASF_OBJECT_HEADER_SIZE, "Object Size" ); |
| 209 | |
| 210 | module->object_level++; |
| 211 | |
| 212 | /* Call the object specific writing function */ |
| 213 | status = asf_object_list[i].pf_func(p_ctx); |
| 214 | |
| 215 | module->object_level--; |
| 216 | |
| 217 | if(status != VC_CONTAINER_SUCCESS) |
| 218 | LOG_DEBUG(p_ctx, "object %s appears to be corrupted" , asf_object_list[i].psz_name); |
| 219 | |
| 220 | return status; |
| 221 | } |
| 222 | |
| 223 | static VC_CONTAINER_STATUS_T ( VC_CONTAINER_T *p_ctx ) |
| 224 | { |
| 225 | VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
| 226 | VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
| 227 | |
| 228 | WRITE_U32(p_ctx, 0, "Number of Header Objects" ); /* FIXME: could use that */ |
| 229 | WRITE_U8(p_ctx, 0, "Reserved1" ); |
| 230 | WRITE_U8(p_ctx, 0, "Reserved2" ); |
| 231 | |
| 232 | status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_FILE_PROPS); |
| 233 | status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_HEADER_EXT); |
| 234 | |
| 235 | for(module->current_track = 0; module->current_track < p_ctx->tracks_num; |
| 236 | module->current_track++) |
| 237 | { |
| 238 | status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_STREAM_PROPS); |
| 239 | } |
| 240 | |
| 241 | /* Codec List */ |
| 242 | /* Content Description */ |
| 243 | /* Stream Bitrate Properties */ |
| 244 | |
| 245 | return status; |
| 246 | } |
| 247 | |
| 248 | static VC_CONTAINER_STATUS_T ( VC_CONTAINER_T *p_ctx ) |
| 249 | { |
| 250 | WRITE_GUID(p_ctx, &guid_null, "Reserved Field 1" ); |
| 251 | WRITE_U16(p_ctx, 0, "Reserved Field 2" ); |
| 252 | |
| 253 | return asf_write_object(p_ctx, ASF_OBJECT_TYPE_HEADER_EXT_INTERNAL); |
| 254 | } |
| 255 | |
| 256 | static VC_CONTAINER_STATUS_T ( VC_CONTAINER_T *p_ctx ) |
| 257 | { |
| 258 | VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
| 259 | VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
| 260 | |
| 261 | for(module->current_track = 0; module->current_track < p_ctx->tracks_num; |
| 262 | module->current_track++) |
| 263 | { |
| 264 | status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_EXT_STREAM_PROPS); |
| 265 | } |
| 266 | |
| 267 | return status; |
| 268 | } |
| 269 | |
| 270 | static VC_CONTAINER_STATUS_T asf_write_object_file_properties( VC_CONTAINER_T *p_ctx ) |
| 271 | { |
| 272 | VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
| 273 | |
| 274 | WRITE_GUID(p_ctx, &guid_null, "File ID" ); |
| 275 | WRITE_U64(p_ctx, 0, "File Size" ); |
| 276 | WRITE_U64(p_ctx, 0, "Creation Date" ); |
| 277 | WRITE_U64(p_ctx, 0, "Data Packets Count" ); |
| 278 | WRITE_U64(p_ctx, 0, "Play Duration" ); |
| 279 | WRITE_U64(p_ctx, 0, "Send Duration" ); |
| 280 | WRITE_U64(p_ctx, 0, "Preroll" ); |
| 281 | WRITE_U32(p_ctx, 0, "Flags" ); |
| 282 | WRITE_U32(p_ctx, module->packet_size, "Minimum Data Packet Size" ); |
| 283 | WRITE_U32(p_ctx, module->packet_size, "Maximum Data Packet Size" ); |
| 284 | WRITE_U32(p_ctx, 0, "Maximum Bitrate" ); |
| 285 | |
| 286 | return VC_CONTAINER_SUCCESS; |
| 287 | } |
| 288 | |
| 289 | static VC_CONTAINER_STATUS_T ( VC_CONTAINER_T *p_ctx, |
| 290 | VC_CONTAINER_TRACK_T *p_track ) |
| 291 | { |
| 292 | uint32_t fourcc; |
| 293 | |
| 294 | /* Write the preamble to the BITMAPINFOHEADER */ |
| 295 | WRITE_U32(p_ctx, p_track->format->type->video.width, "Encoded Image Width" ); |
| 296 | WRITE_U32(p_ctx, p_track->format->type->video.height, "Encoded Image Height" ); |
| 297 | WRITE_U8(p_ctx, 0, "Reserved Flags" ); |
| 298 | WRITE_U16(p_ctx, 40 + p_track->format->extradata_size, "Format Data Size" ); |
| 299 | |
| 300 | /* Write BITMAPINFOHEADER structure */ |
| 301 | WRITE_U32(p_ctx, 40 + p_track->format->extradata_size, "Format Data Size" ); |
| 302 | WRITE_U32(p_ctx, p_track->format->type->video.width, "Image Width" ); |
| 303 | WRITE_U32(p_ctx, p_track->format->type->video.height, "Image Height" ); |
| 304 | WRITE_U16(p_ctx, 0, "Reserved" ); |
| 305 | WRITE_U16(p_ctx, 0, "Bits Per Pixel Count" ); |
| 306 | fourcc = codec_to_fourcc(p_track->format->codec); |
| 307 | WRITE_BYTES(p_ctx, (char *)&fourcc, 4); /* Compression ID */ |
| 308 | LOG_FORMAT(p_ctx, "Compression ID: %4.4s" , (char *)&fourcc); |
| 309 | WRITE_U32(p_ctx, 0, "Image Size" ); |
| 310 | WRITE_U32(p_ctx, 0, "Horizontal Pixels Per Meter" ); |
| 311 | WRITE_U32(p_ctx, 0, "Vertical Pixels Per Meter" ); |
| 312 | WRITE_U32(p_ctx, 0, "Colors Used Count" ); |
| 313 | WRITE_U32(p_ctx, 0, "Important Colors Count" ); |
| 314 | |
| 315 | WRITE_BYTES(p_ctx, p_track->format->extradata, p_track->format->extradata_size); |
| 316 | |
| 317 | return VC_CONTAINER_SUCCESS; |
| 318 | } |
| 319 | |
| 320 | static VC_CONTAINER_STATUS_T asf_write_waveformatex( VC_CONTAINER_T *p_ctx, |
| 321 | VC_CONTAINER_TRACK_T *p_track) |
| 322 | { |
| 323 | /* Write WAVEFORMATEX structure */ |
| 324 | WRITE_U16(p_ctx, codec_to_waveformat(p_track->format->codec), "Codec ID" ); |
| 325 | WRITE_U16(p_ctx, p_track->format->type->audio.channels, "Number of Channels" ); |
| 326 | WRITE_U32(p_ctx, p_track->format->type->audio.sample_rate, "Samples per Second" ); |
| 327 | WRITE_U32(p_ctx, p_track->format->bitrate, "Average Number of Bytes Per Second" ); |
| 328 | WRITE_U16(p_ctx, p_track->format->type->audio.block_align, "Block Alignment" ); |
| 329 | WRITE_U16(p_ctx, p_track->format->type->audio.bits_per_sample, "Bits Per Sample" ); |
| 330 | WRITE_U16(p_ctx, p_track->format->extradata_size, "Codec Specific Data Size" ); |
| 331 | WRITE_BYTES(p_ctx, p_track->format->extradata, p_track->format->extradata_size); |
| 332 | |
| 333 | return VC_CONTAINER_SUCCESS; |
| 334 | } |
| 335 | |
| 336 | static VC_CONTAINER_STATUS_T asf_write_object_stream_properties( VC_CONTAINER_T *p_ctx ) |
| 337 | { |
| 338 | VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
| 339 | VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
| 340 | unsigned int track = module->current_track, ts_size = 0; |
| 341 | const GUID_T *p_guid = &guid_null; |
| 342 | |
| 343 | if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) |
| 344 | { |
| 345 | p_guid = &asf_guid_stream_type_video; |
| 346 | ts_size = 11 + 40 + p_ctx->tracks[track]->format->extradata_size; |
| 347 | } |
| 348 | else if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) |
| 349 | { |
| 350 | p_guid = &asf_guid_stream_type_audio; |
| 351 | ts_size = 18 + p_ctx->tracks[track]->format->extradata_size; |
| 352 | } |
| 353 | |
| 354 | WRITE_GUID(p_ctx, p_guid, "Stream Type" ); |
| 355 | WRITE_GUID(p_ctx, &asf_guid_error_correction, "Error Correction Type" ); |
| 356 | WRITE_U64(p_ctx, 0, "Time Offset" ); |
| 357 | WRITE_U32(p_ctx, ts_size, "Type-Specific Data Length" ); |
| 358 | WRITE_U32(p_ctx, 0, "Error Correction Data Length" ); |
| 359 | WRITE_U16(p_ctx, track + 1, "Flags" ); |
| 360 | WRITE_U32(p_ctx, 0, "Reserved" ); |
| 361 | |
| 362 | /* Type-Specific Data */ |
| 363 | if(ts_size) |
| 364 | { |
| 365 | if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) |
| 366 | status = asf_write_bitmapinfoheader( p_ctx, p_ctx->tracks[track]); |
| 367 | else if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) |
| 368 | status = asf_write_waveformatex( p_ctx, p_ctx->tracks[track]); |
| 369 | } |
| 370 | |
| 371 | /* Error Correction Data */ |
| 372 | |
| 373 | return status; |
| 374 | } |
| 375 | |
| 376 | static VC_CONTAINER_STATUS_T asf_write_object_ext_stream_properties( VC_CONTAINER_T *p_ctx ) |
| 377 | { |
| 378 | VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
| 379 | |
| 380 | WRITE_U64(p_ctx, 0, "Start Time" ); |
| 381 | WRITE_U64(p_ctx, 0, "End Time" ); |
| 382 | WRITE_U32(p_ctx, 0, "Data Bitrate" ); |
| 383 | WRITE_U32(p_ctx, 0, "Buffer Size" ); |
| 384 | WRITE_U32(p_ctx, 0, "Initial Buffer Fullness" ); |
| 385 | WRITE_U32(p_ctx, 0, "Alternate Data Bitrate" ); |
| 386 | WRITE_U32(p_ctx, 0, "Alternate Buffer Size" ); |
| 387 | WRITE_U32(p_ctx, 0, "Alternate Initial Buffer Fullness" ); |
| 388 | WRITE_U32(p_ctx, 0, "Maximum Object Size" ); |
| 389 | WRITE_U32(p_ctx, 0, "Flags" ); |
| 390 | WRITE_U16(p_ctx, module->current_track + 1, "Stream Number" ); |
| 391 | WRITE_U16(p_ctx, 0, "Stream Language ID Index" ); |
| 392 | WRITE_U64(p_ctx, 0, "Average Time Per Frame" ); |
| 393 | WRITE_U16(p_ctx, 0, "Stream Name Count" ); |
| 394 | WRITE_U16(p_ctx, 0, "Payload Extension System Count" ); |
| 395 | /* Stream Names */ |
| 396 | /* Payload Extension Systems */ |
| 397 | /* Stream Properties Object */ |
| 398 | |
| 399 | return VC_CONTAINER_SUCCESS; |
| 400 | } |
| 401 | |
| 402 | static VC_CONTAINER_STATUS_T asf_write_object_index( VC_CONTAINER_T *p_ctx ) |
| 403 | { |
| 404 | VC_CONTAINER_PARAM_UNUSED(p_ctx); |
| 405 | return VC_CONTAINER_SUCCESS; |
| 406 | } |
| 407 | |
| 408 | static VC_CONTAINER_STATUS_T asf_write_object_simple_index( VC_CONTAINER_T *p_ctx ) |
| 409 | { |
| 410 | VC_CONTAINER_PARAM_UNUSED(p_ctx); |
| 411 | return VC_CONTAINER_SUCCESS; |
| 412 | } |
| 413 | |
| 414 | static VC_CONTAINER_STATUS_T asf_write_object_data( VC_CONTAINER_T *p_ctx ) |
| 415 | { |
| 416 | VC_CONTAINER_PARAM_UNUSED(p_ctx); |
| 417 | return VC_CONTAINER_SUCCESS; |
| 418 | } |
| 419 | |
| 420 | /*****************************************************************************/ |
| 421 | static VC_CONTAINER_STATUS_T ( VC_CONTAINER_T *p_ctx ) |
| 422 | { |
| 423 | VC_CONTAINER_STATUS_T status; |
| 424 | |
| 425 | /* TODO Sanity check the tracks */ |
| 426 | |
| 427 | status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_HEADER); |
| 428 | status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_DATA); |
| 429 | |
| 430 | return status; |
| 431 | } |
| 432 | |
| 433 | /*****************************************************************************/ |
| 434 | static VC_CONTAINER_STATUS_T asf_writer_write( VC_CONTAINER_T *p_ctx, |
| 435 | VC_CONTAINER_PACKET_T *p_packet ) |
| 436 | { |
| 437 | VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
| 438 | VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
| 439 | VC_CONTAINER_PARAM_UNUSED(p_packet); |
| 440 | |
| 441 | if(!module->b_header_done) |
| 442 | { |
| 443 | module->b_header_done = true; |
| 444 | status = asf_write_header(p_ctx); |
| 445 | } |
| 446 | |
| 447 | return status; |
| 448 | } |
| 449 | |
| 450 | /*****************************************************************************/ |
| 451 | static VC_CONTAINER_STATUS_T asf_writer_close( VC_CONTAINER_T *p_ctx ) |
| 452 | { |
| 453 | VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
| 454 | |
| 455 | for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) |
| 456 | vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); |
| 457 | |
| 458 | vc_container_writer_extraio_delete(p_ctx, &module->null); |
| 459 | free(module); |
| 460 | |
| 461 | return VC_CONTAINER_SUCCESS; |
| 462 | } |
| 463 | |
| 464 | /*****************************************************************************/ |
| 465 | static VC_CONTAINER_STATUS_T asf_writer_add_track( VC_CONTAINER_T *p_ctx, VC_CONTAINER_ES_FORMAT_T *format ) |
| 466 | { |
| 467 | VC_CONTAINER_STATUS_T status; |
| 468 | VC_CONTAINER_TRACK_T *track; |
| 469 | |
| 470 | /* TODO check we support this format */ |
| 471 | |
| 472 | if(!(format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED)) |
| 473 | return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; |
| 474 | |
| 475 | /* Allocate and initialise track data */ |
| 476 | if(p_ctx->tracks_num >= ASF_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; |
| 477 | p_ctx->tracks[p_ctx->tracks_num] = track = |
| 478 | vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); |
| 479 | if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; |
| 480 | |
| 481 | if(format->extradata_size) |
| 482 | { |
| 483 | status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size ); |
| 484 | if(status) goto error; |
| 485 | } |
| 486 | |
| 487 | vc_container_format_copy(track->format, format, format->extradata_size); |
| 488 | p_ctx->tracks_num++; |
| 489 | return VC_CONTAINER_SUCCESS; |
| 490 | |
| 491 | error: |
| 492 | vc_container_free_track(p_ctx, track); |
| 493 | return status; |
| 494 | } |
| 495 | |
| 496 | /*****************************************************************************/ |
| 497 | static VC_CONTAINER_STATUS_T asf_writer_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args ) |
| 498 | { |
| 499 | VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
| 500 | VC_CONTAINER_STATUS_T status; |
| 501 | |
| 502 | switch(operation) |
| 503 | { |
| 504 | case VC_CONTAINER_CONTROL_TRACK_ADD: |
| 505 | { |
| 506 | VC_CONTAINER_ES_FORMAT_T *p_format = |
| 507 | (VC_CONTAINER_ES_FORMAT_T *)va_arg( args, VC_CONTAINER_ES_FORMAT_T * ); |
| 508 | return asf_writer_add_track(p_ctx, p_format); |
| 509 | } |
| 510 | |
| 511 | case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: |
| 512 | if(!module->b_header_done) |
| 513 | { |
| 514 | status = asf_write_header(p_ctx); |
| 515 | if(status != VC_CONTAINER_SUCCESS) return status; |
| 516 | module->b_header_done = true; |
| 517 | } |
| 518 | return VC_CONTAINER_SUCCESS; |
| 519 | |
| 520 | default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; |
| 521 | } |
| 522 | } |
| 523 | |
| 524 | /****************************************************************************** |
| 525 | Global function definitions. |
| 526 | ******************************************************************************/ |
| 527 | |
| 528 | VC_CONTAINER_STATUS_T asf_writer_open( VC_CONTAINER_T *p_ctx ) |
| 529 | { |
| 530 | VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
| 531 | const char *extension = vc_uri_path_extension(p_ctx->priv->uri); |
| 532 | VC_CONTAINER_MODULE_T *module = 0; |
| 533 | unsigned int i; |
| 534 | |
| 535 | /* Check if the user has specified a container */ |
| 536 | vc_uri_find_query(p_ctx->priv->uri, 0, "container" , &extension); |
| 537 | |
| 538 | /* Check we're the right writer for this */ |
| 539 | if(!extension) |
| 540 | return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
| 541 | if(strcasecmp(extension, "asf" ) && strcasecmp(extension, "wmv" ) && |
| 542 | strcasecmp(extension, "wma" )) |
| 543 | return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
| 544 | |
| 545 | /* Allocate our context */ |
| 546 | module = malloc(sizeof(*module)); |
| 547 | if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } |
| 548 | memset(module, 0, sizeof(*module)); |
| 549 | p_ctx->priv->module = module; |
| 550 | p_ctx->tracks = module->tracks; |
| 551 | |
| 552 | /* Create a null i/o writer to help us out in writing our data */ |
| 553 | status = vc_container_writer_extraio_create_null(p_ctx, &module->null); |
| 554 | if(status != VC_CONTAINER_SUCCESS) goto error; |
| 555 | |
| 556 | /* We'll only write the header once we've got all our tracks */ |
| 557 | |
| 558 | p_ctx->priv->pf_close = asf_writer_close; |
| 559 | p_ctx->priv->pf_write = asf_writer_write; |
| 560 | p_ctx->priv->pf_control = asf_writer_control; |
| 561 | return VC_CONTAINER_SUCCESS; |
| 562 | |
| 563 | error: |
| 564 | LOG_DEBUG(p_ctx, "asf: error opening stream" ); |
| 565 | for(i = 0; i < ASF_TRACKS_MAX && p_ctx->tracks && p_ctx->tracks[i]; i++) |
| 566 | vc_container_free_track(p_ctx, p_ctx->tracks[i]); |
| 567 | free(module); |
| 568 | return status; |
| 569 | } |
| 570 | |
| 571 | /******************************************************************************** |
| 572 | Entrypoint function |
| 573 | ********************************************************************************/ |
| 574 | |
| 575 | #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) |
| 576 | # pragma weak writer_open asf_writer_open |
| 577 | #endif |
| 578 | |