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 "raw_video_common.h"
38 
39 /******************************************************************************
40 Defines.
41 ******************************************************************************/
42 
43 /******************************************************************************
44 Type definitions
45 ******************************************************************************/
46 typedef struct VC_CONTAINER_MODULE_T
47 {
48    VC_CONTAINER_TRACK_T *track;
49    bool yuv4mpeg2;
50    bool header_done;
51    bool non_standard;
52 
53 } VC_CONTAINER_MODULE_T;
54 
55 /******************************************************************************
56 Function prototypes
57 ******************************************************************************/
58 VC_CONTAINER_STATUS_T rawvideo_writer_open( VC_CONTAINER_T * );
59 
60 /******************************************************************************
61 Local Functions
62 ******************************************************************************/
rawvideo_write_header(VC_CONTAINER_T * ctx)63 static VC_CONTAINER_STATUS_T rawvideo_write_header( VC_CONTAINER_T *ctx )
64 {
65    VC_CONTAINER_MODULE_T *module = ctx->priv->module;
66    unsigned int size;
67    char line[128];
68    const char *id;
69 
70    size = snprintf(line, sizeof(line), "YUV4MPEG2 W%i H%i",
71       ctx->tracks[0]->format->type->video.width,
72       ctx->tracks[0]->format->type->video.height);
73    if (size >= sizeof(line))
74       return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
75    WRITE_BYTES(ctx, line, size);
76 
77    if (ctx->tracks[0]->format->type->video.frame_rate_num &&
78        ctx->tracks[0]->format->type->video.frame_rate_den)
79    {
80       size = snprintf(line, sizeof(line), " F%i:%i",
81          ctx->tracks[0]->format->type->video.frame_rate_num,
82          ctx->tracks[0]->format->type->video.frame_rate_den);
83       if (size >= sizeof(line))
84          return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
85       WRITE_BYTES(ctx, line, size);
86    }
87 
88    if (ctx->tracks[0]->format->type->video.par_num &&
89        ctx->tracks[0]->format->type->video.par_den)
90    {
91       size = snprintf(line, sizeof(line), " A%i:%i",
92          ctx->tracks[0]->format->type->video.par_num,
93          ctx->tracks[0]->format->type->video.par_den);
94       if (size >= sizeof(line))
95          return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
96       WRITE_BYTES(ctx, line, size);
97    }
98 
99    if (to_yuv4mpeg2(ctx->tracks[0]->format->codec, &id, 0, 0))
100    {
101       size = snprintf(line, sizeof(line), " C%s", id);
102    }
103    else
104    {
105       module->non_standard = true;
106       size = snprintf(line, sizeof(line), " C%4.4s",
107          (char *)&ctx->tracks[0]->format->codec);
108    }
109    if (size >= sizeof(line))
110       return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
111    WRITE_BYTES(ctx, line, size);
112 
113    _WRITE_U8(ctx, 0x0a);
114    module->header_done = true;
115    return STREAM_STATUS(ctx);
116 }
117 
simple_write_add_track(VC_CONTAINER_T * ctx,VC_CONTAINER_ES_FORMAT_T * format)118 static VC_CONTAINER_STATUS_T simple_write_add_track( VC_CONTAINER_T *ctx,
119    VC_CONTAINER_ES_FORMAT_T *format )
120 {
121    VC_CONTAINER_STATUS_T status;
122 
123    /* Sanity check that we support the type of track being created */
124    if (ctx->tracks_num)
125       return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
126    if (format->es_type != VC_CONTAINER_ES_TYPE_VIDEO)
127       return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED;
128 
129    /* Allocate and initialise track data */
130    ctx->tracks[0] = vc_container_allocate_track(ctx, 0);
131    if (!ctx->tracks[0])
132       return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
133 
134    status = vc_container_track_allocate_extradata(ctx,
135       ctx->tracks[0], format->extradata_size);
136    if(status != VC_CONTAINER_SUCCESS)
137       return status;
138 
139    vc_container_format_copy(ctx->tracks[0]->format, format,
140       format->extradata_size);
141    ctx->tracks_num++;
142    return VC_CONTAINER_SUCCESS;
143 }
144 
145 /*****************************************************************************
146 Functions exported as part of the Container Module API
147  *****************************************************************************/
rawvideo_writer_close(VC_CONTAINER_T * ctx)148 static VC_CONTAINER_STATUS_T rawvideo_writer_close( VC_CONTAINER_T *ctx )
149 {
150    VC_CONTAINER_MODULE_T *module = ctx->priv->module;
151    for (; ctx->tracks_num > 0; ctx->tracks_num--)
152       vc_container_free_track(ctx, ctx->tracks[ctx->tracks_num-1]);
153    free(module);
154    return VC_CONTAINER_SUCCESS;
155 }
156 
157 /*****************************************************************************/
rawvideo_writer_write(VC_CONTAINER_T * ctx,VC_CONTAINER_PACKET_T * packet)158 static VC_CONTAINER_STATUS_T rawvideo_writer_write( VC_CONTAINER_T *ctx,
159    VC_CONTAINER_PACKET_T *packet )
160 {
161    VC_CONTAINER_MODULE_T *module = ctx->priv->module;
162    VC_CONTAINER_STATUS_T status;
163 
164    if (module->yuv4mpeg2 && !module->header_done)
165    {
166       status = rawvideo_write_header(ctx);
167       if (status != VC_CONTAINER_SUCCESS)
168          return status;
169    }
170 
171    if (module->yuv4mpeg2 &&
172        (packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START))
173    {
174       /* Write the metadata */
175       WRITE_BYTES(ctx, "FRAME", sizeof("FRAME")-1);
176 
177       /* For formats not supported by the YUV4MPEG2 spec, we prepend
178        * each frame with its size */
179       if (module->non_standard)
180       {
181          unsigned int size;
182          char line[32];
183          size = snprintf(line, sizeof(line), " S%i",
184             packet->frame_size ? packet->frame_size : packet->size);
185          if (size < sizeof(line))
186             WRITE_BYTES(ctx, line, size);
187       }
188 
189       _WRITE_U8(ctx, 0x0a);
190    }
191 
192    /* Write the elementary stream */
193    WRITE_BYTES(ctx, packet->data, packet->size);
194 
195    return STREAM_STATUS(ctx);
196 }
197 
198 /*****************************************************************************/
rawvideo_writer_control(VC_CONTAINER_T * ctx,VC_CONTAINER_CONTROL_T operation,va_list args)199 static VC_CONTAINER_STATUS_T rawvideo_writer_control( VC_CONTAINER_T *ctx,
200    VC_CONTAINER_CONTROL_T operation, va_list args )
201 {
202     VC_CONTAINER_MODULE_T *module = ctx->priv->module;
203     VC_CONTAINER_ES_FORMAT_T *format;
204 
205    switch (operation)
206    {
207    case VC_CONTAINER_CONTROL_TRACK_ADD:
208       format = (VC_CONTAINER_ES_FORMAT_T *)va_arg(args, VC_CONTAINER_ES_FORMAT_T *);
209       return simple_write_add_track(ctx, format);
210 
211    case VC_CONTAINER_CONTROL_TRACK_ADD_DONE:
212       return module->yuv4mpeg2 ?
213          rawvideo_write_header( ctx ) : VC_CONTAINER_SUCCESS;
214 
215    default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
216    }
217 }
218 
219 /*****************************************************************************/
rawvideo_writer_open(VC_CONTAINER_T * ctx)220 VC_CONTAINER_STATUS_T rawvideo_writer_open( VC_CONTAINER_T *ctx )
221 {
222    VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID;
223    const char *extension = vc_uri_path_extension(ctx->priv->uri);
224    VC_CONTAINER_MODULE_T *module;
225    bool yuv4mpeg2 = false;
226 
227    /* Check if the user has specified a container */
228    vc_uri_find_query(ctx->priv->uri, 0, "container", &extension);
229 
230    /* Check we're the right writer for this */
231    if(!extension)
232       return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
233    if(!strcasecmp(extension, "y4m") || !strcasecmp(extension, "yuv4mpeg2"))
234       yuv4mpeg2 = true;
235    if(!yuv4mpeg2 && strcasecmp(extension, "yuv"))
236       return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
237 
238    LOG_DEBUG(ctx, "using rawvideo writer");
239 
240    /* Allocate our context */
241    module = malloc(sizeof(*module));
242    if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
243    memset(module, 0, sizeof(*module));
244    ctx->priv->module = module;
245    ctx->tracks = &module->track;
246    module->yuv4mpeg2 = yuv4mpeg2;
247 
248    ctx->priv->pf_close = rawvideo_writer_close;
249    ctx->priv->pf_write = rawvideo_writer_write;
250    ctx->priv->pf_control = rawvideo_writer_control;
251    return VC_CONTAINER_SUCCESS;
252 
253  error:
254    LOG_DEBUG(ctx, "rawvideo: error opening stream (%i)", status);
255    return status;
256 }
257 
258 /********************************************************************************
259  Entrypoint function
260  ********************************************************************************/
261 
262 #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
263 # pragma weak writer_open rawvideo_writer_open
264 #endif
265