1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27#include <stdio.h>
28#include <stdlib.h>
29#include <stdarg.h>
30#include <string.h>
31
32#include "containers/core/containers_private.h"
33#include "containers/core/containers_io_helpers.h"
34#include "containers/core/containers_utils.h"
35#include "containers/core/containers_logging.h"
36
37#include "simple_common.h"
38
39/******************************************************************************
40Defines.
41******************************************************************************/
42#define MAX_TRACKS 4
43#define MAX_LINE_SIZE 512
44
45#define ES_SUFFIX "%s.%2.2i.%4.4s"
46#define ES_SUFFIX_SIZE 8
47
48/******************************************************************************
49Type definitions
50******************************************************************************/
51typedef struct VC_CONTAINER_TRACK_MODULE_T
52{
53 VC_CONTAINER_IO_T *io;
54 char *uri;
55
56 bool config_done;
57
58} VC_CONTAINER_TRACK_MODULE_T;
59
60typedef struct VC_CONTAINER_MODULE_T
61{
62 char line[MAX_LINE_SIZE + 1];
63
64 VC_CONTAINER_TRACK_T *tracks[MAX_TRACKS];
65 bool header_done;
66
67} VC_CONTAINER_MODULE_T;
68
69/******************************************************************************
70Function prototypes
71******************************************************************************/
72VC_CONTAINER_STATUS_T simple_writer_open( VC_CONTAINER_T * );
73static VC_CONTAINER_STATUS_T simple_writer_write( VC_CONTAINER_T *ctx,
74 VC_CONTAINER_PACKET_T *packet );
75
76/******************************************************************************
77Local Functions
78******************************************************************************/
79static VC_CONTAINER_STATUS_T simple_write_line( VC_CONTAINER_T *ctx,
80 const char *format, ...)
81{
82 VC_CONTAINER_MODULE_T *module = ctx->priv->module;
83 va_list args;
84 int result;
85
86 va_start(args, format);
87 result = vsnprintf(module->line, sizeof(module->line), format, args);
88 va_end(args);
89
90 if (result >= (int)sizeof(module->line))
91 return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
92
93 WRITE_BYTES(ctx, module->line, result);
94 _WRITE_U8(ctx, '\n');
95 return STREAM_STATUS(ctx);
96}
97
98static VC_CONTAINER_STATUS_T simple_write_header( VC_CONTAINER_T *ctx )
99{
100 unsigned int i;
101
102 simple_write_line(ctx, SIGNATURE_STRING);
103
104 for (i = 0; i < ctx->tracks_num; i++)
105 {
106 VC_CONTAINER_TRACK_T *track = ctx->tracks[i];
107
108 if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
109 {
110 simple_write_line(ctx, "TRACK video, %4.4s, %i, %i",
111 (char *)&track->format->codec,
112 (int)track->format->type->video.width,
113 (int)track->format->type->video.height);
114 if ((track->format->type->video.visible_width &&
115 track->format->type->video.visible_width !=
116 track->format->type->video.width) ||
117 (track->format->type->video.visible_height &&
118 track->format->type->video.visible_height !=
119 track->format->type->video.height))
120 simple_write_line(ctx, CONFIG_VIDEO_CROP" %i, %i",
121 track->format->type->video.visible_width,
122 track->format->type->video.visible_height);
123 if (track->format->type->video.par_num &&
124 track->format->type->video.par_den)
125 simple_write_line(ctx, CONFIG_VIDEO_ASPECT" %i, %i",
126 track->format->type->video.par_num,
127 track->format->type->video.par_den);
128 }
129 else if (track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO)
130 {
131 simple_write_line(ctx, "TRACK audio, %4.4s, %i, %i, %i, %i",
132 (char *)&track->format->codec,
133 (int)track->format->type->audio.channels,
134 (int)track->format->type->audio.sample_rate,
135 (int)track->format->type->audio.bits_per_sample,
136 (int)track->format->type->audio.block_align);
137 }
138 else if (track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO)
139 {
140 simple_write_line(ctx, "TRACK subpicture, %4.4s, %i",
141 (char *)&track->format->codec,
142 (int)track->format->type->subpicture.encoding);
143 }
144 else
145 {
146 simple_write_line(ctx, "TRACK unknown, %4.4s",
147 (char *)&track->format->codec);
148 }
149
150 simple_write_line(ctx, CONFIG_URI" %s", track->priv->module->io->uri);
151 if (track->format->codec_variant)
152 simple_write_line(ctx, CONFIG_CODEC_VARIANT" %4.4s",
153 (char *)&track->format->codec_variant);
154 if (track->format->bitrate)
155 simple_write_line(ctx, CONFIG_BITRATE" %i", track->format->bitrate);
156 if (!(track->format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED))
157 simple_write_line(ctx, CONFIG_UNFRAMED);
158 }
159
160 simple_write_line(ctx, SIGNATURE_END_STRING);
161
162 ctx->priv->module->header_done = true;
163 return STREAM_STATUS(ctx);
164}
165
166static VC_CONTAINER_STATUS_T simple_write_config( VC_CONTAINER_T *ctx,
167 unsigned int track_num, VC_CONTAINER_PACKET_T *pkt)
168{
169 VC_CONTAINER_TRACK_T *track = ctx->tracks[track_num];
170 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
171 VC_CONTAINER_PACKET_T packet;
172
173 track->priv->module->config_done = true;
174
175 if (track->format->extradata_size)
176 {
177 packet.size = track->format->extradata_size;
178 packet.data = track->format->extradata;
179 packet.track = track_num;
180 packet.pts = pkt ? pkt->pts : VC_CONTAINER_TIME_UNKNOWN;
181 packet.flags = 0;
182 packet.flags |= VC_CONTAINER_PACKET_FLAG_CONFIG;
183
184 status = simple_writer_write(ctx, &packet);
185 }
186
187 return status;
188}
189
190static VC_CONTAINER_STATUS_T simple_write_add_track( VC_CONTAINER_T *ctx,
191 VC_CONTAINER_ES_FORMAT_T *format )
192{
193 VC_CONTAINER_TRACK_T *track = NULL;
194 VC_CONTAINER_STATUS_T status;
195 const char *uri = vc_uri_path(ctx->priv->uri);
196 unsigned int uri_size = strlen(uri);
197
198 /* Allocate and initialise track data */
199 if (ctx->tracks_num >= MAX_TRACKS)
200 return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
201
202 ctx->tracks[ctx->tracks_num] = track =
203 vc_container_allocate_track(ctx, sizeof(VC_CONTAINER_TRACK_MODULE_T) +
204 uri_size + ES_SUFFIX_SIZE + 1);
205 if (!track)
206 return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
207
208 if (format->extradata_size)
209 {
210 status = vc_container_track_allocate_extradata(ctx, track, format->extradata_size);
211 if (status != VC_CONTAINER_SUCCESS)
212 goto error;
213 }
214 vc_container_format_copy(track->format, format, format->extradata_size);
215
216 track->priv->module->uri = (char *)&track->priv->module[1];
217 snprintf(track->priv->module->uri, uri_size + ES_SUFFIX_SIZE + 1,
218 ES_SUFFIX, uri, ctx->tracks_num, (char *)&track->format->codec);
219
220 LOG_DEBUG(ctx, "opening elementary stream: %s", track->priv->module->uri);
221 track->priv->module->io = vc_container_io_open(track->priv->module->uri,
222 VC_CONTAINER_IO_MODE_WRITE, &status);
223 if (status != VC_CONTAINER_SUCCESS)
224 {
225 LOG_ERROR(ctx, "error opening elementary stream: %s",
226 track->priv->module->uri);
227 goto error;
228 }
229
230 ctx->tracks_num++;
231 return VC_CONTAINER_SUCCESS;
232
233 error:
234 if (track)
235 vc_container_free_track(ctx, track);
236 return status;
237}
238
239/*****************************************************************************
240Functions exported as part of the Container Module API
241 *****************************************************************************/
242static VC_CONTAINER_STATUS_T simple_writer_close( VC_CONTAINER_T *ctx )
243{
244 VC_CONTAINER_MODULE_T *module = ctx->priv->module;
245 for (; ctx->tracks_num > 0; ctx->tracks_num--)
246 {
247 vc_container_io_close(ctx->tracks[ctx->tracks_num-1]->priv->module->io);
248 vc_container_free_track(ctx, ctx->tracks[ctx->tracks_num-1]);
249 }
250 free(module);
251 return VC_CONTAINER_SUCCESS;
252}
253
254/*****************************************************************************/
255static VC_CONTAINER_STATUS_T simple_writer_write( VC_CONTAINER_T *ctx,
256 VC_CONTAINER_PACKET_T *packet )
257{
258 VC_CONTAINER_STATUS_T status;
259
260 if (!ctx->priv->module->header_done)
261 {
262 status = simple_write_header(ctx);
263 if (status != VC_CONTAINER_SUCCESS)
264 return status;
265 }
266
267 if (!ctx->tracks[packet->track]->priv->module->config_done)
268 {
269 status = simple_write_config(ctx, packet->track, packet);
270 if (status != VC_CONTAINER_SUCCESS)
271 return status;
272 }
273
274 /* Write the metadata */
275 status = simple_write_line(ctx, "%i %i %"PRIi64" 0x%x",
276 (int)packet->track, (int)packet->size, packet->pts, packet->flags);
277 if (status != VC_CONTAINER_SUCCESS)
278 return status;
279
280 /* Write the elementary stream */
281 vc_container_io_write(ctx->tracks[packet->track]->priv->module->io,
282 packet->data, packet->size);
283
284 return STREAM_STATUS(ctx);
285}
286
287/*****************************************************************************/
288static VC_CONTAINER_STATUS_T simple_writer_control( VC_CONTAINER_T *ctx,
289 VC_CONTAINER_CONTROL_T operation, va_list args )
290{
291 VC_CONTAINER_ES_FORMAT_T *format;
292
293 switch (operation)
294 {
295 case VC_CONTAINER_CONTROL_TRACK_ADD:
296 format = (VC_CONTAINER_ES_FORMAT_T *)va_arg(args, VC_CONTAINER_ES_FORMAT_T *);
297 return simple_write_add_track(ctx, format);
298
299 case VC_CONTAINER_CONTROL_TRACK_ADD_DONE:
300 simple_write_header( ctx );
301 return VC_CONTAINER_SUCCESS;
302
303 default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
304 }
305}
306
307/*****************************************************************************/
308VC_CONTAINER_STATUS_T simple_writer_open( VC_CONTAINER_T *ctx )
309{
310 VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID;
311 const char *extension = vc_uri_path_extension(ctx->priv->uri);
312 VC_CONTAINER_MODULE_T *module;
313
314 /* Check if the user has specified a container */
315 vc_uri_find_query(ctx->priv->uri, 0, "container", &extension);
316
317 /* Check we're the right writer for this */
318 if(!extension)
319 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
320 if(strcasecmp(extension, "smpl") && strcasecmp(extension, "simple"))
321 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
322
323 LOG_DEBUG(ctx, "using simple writer");
324
325 /* Allocate our context */
326 module = malloc(sizeof(*module));
327 if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
328 memset(module, 0, sizeof(*module));
329 ctx->priv->module = module;
330 ctx->tracks = module->tracks;
331
332 ctx->priv->pf_close = simple_writer_close;
333 ctx->priv->pf_write = simple_writer_write;
334 ctx->priv->pf_control = simple_writer_control;
335 return VC_CONTAINER_SUCCESS;
336
337 error:
338 LOG_DEBUG(ctx, "simple: error opening stream (%i)", status);
339 return status;
340}
341
342/********************************************************************************
343 Entrypoint function
344 ********************************************************************************/
345
346#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
347# pragma weak writer_open simple_writer_open
348#endif
349