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 "raw_video_common.h"
38
39/******************************************************************************
40Defines.
41******************************************************************************/
42
43/******************************************************************************
44Type definitions
45******************************************************************************/
46typedef 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/******************************************************************************
56Function prototypes
57******************************************************************************/
58VC_CONTAINER_STATUS_T rawvideo_writer_open( VC_CONTAINER_T * );
59
60/******************************************************************************
61Local Functions
62******************************************************************************/
63static 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
118static 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/*****************************************************************************
146Functions exported as part of the Container Module API
147 *****************************************************************************/
148static 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/*****************************************************************************/
158static 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/*****************************************************************************/
199static 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/*****************************************************************************/
220VC_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