Initial version of libomxil-vc4 for RPI3
[platform/adaptation/broadcom/libomxil-vc4.git] / containers / simple / simple_writer.c
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 <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 /******************************************************************************
40 Defines.
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 /******************************************************************************
49 Type definitions
50 ******************************************************************************/
51 typedef 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
60 typedef 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 /******************************************************************************
70 Function prototypes
71 ******************************************************************************/
72 VC_CONTAINER_STATUS_T simple_writer_open( VC_CONTAINER_T * );
73 static VC_CONTAINER_STATUS_T simple_writer_write( VC_CONTAINER_T *ctx,
74    VC_CONTAINER_PACKET_T *packet );
75
76 /******************************************************************************
77 Local Functions
78 ******************************************************************************/
79 static 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
98 static 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
166 static 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
190 static 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 /*****************************************************************************
240 Functions exported as part of the Container Module API
241  *****************************************************************************/
242 static 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 /*****************************************************************************/
255 static 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 /*****************************************************************************/
288 static 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 /*****************************************************************************/
308 VC_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