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
28#include "mmal.h"
29#include "core/mmal_component_private.h"
30#include "core/mmal_port_private.h"
31#include "mmal_logging.h"
32
33#define SPLITTER_OUTPUT_PORTS_NUM 4 /* 4 should do for now */
34
35/*****************************************************************************/
36typedef struct MMAL_COMPONENT_MODULE_T
37{
38 uint32_t enabled_flags; /**< Flags indicating which output port is enabled */
39 uint32_t sent_flags; /**< Flags indicating which output port we've already sent data to */
40 MMAL_BOOL_T error; /**< Error state */
41
42} MMAL_COMPONENT_MODULE_T;
43
44typedef struct MMAL_PORT_MODULE_T
45{
46 MMAL_QUEUE_T *queue; /**< queue for the buffers sent to the ports */
47
48} MMAL_PORT_MODULE_T;
49
50/*****************************************************************************/
51
52/** Destroy a previously created component */
53static MMAL_STATUS_T splitter_component_destroy(MMAL_COMPONENT_T *component)
54{
55 unsigned int i;
56
57 for(i = 0; i < component->input_num; i++)
58 if(component->input[i]->priv->module->queue)
59 mmal_queue_destroy(component->input[i]->priv->module->queue);
60 if(component->input_num)
61 mmal_ports_free(component->input, component->input_num);
62
63 for(i = 0; i < component->output_num; i++)
64 if(component->output[i]->priv->module->queue)
65 mmal_queue_destroy(component->output[i]->priv->module->queue);
66 if(component->output_num)
67 mmal_ports_free(component->output, component->output_num);
68
69 vcos_free(component->priv->module);
70 return MMAL_SUCCESS;
71}
72
73/** Enable processing on a port */
74static MMAL_STATUS_T splitter_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
75{
76#if 0
77 MMAL_COMPONENT_T *component = port->component;
78 uint32_t buffer_num, buffer_size;
79 unsigned int i;
80
81 /* Find the max and apply that to all ports */
82 buffer_num = component->input[0]->buffer_num;
83 buffer_size = component->input[0]->buffer_size;
84 for (i = 0; i < component->output_num; i++)
85 {
86 buffer_num = MMAL_MAX(buffer_num, component->output[i]->buffer_num);
87 buffer_size = MMAL_MAX(buffer_num, component->output[i]->buffer_size);
88 }
89 component->input[0]->buffer_num = buffer_num;
90 component->input[0]->buffer_size = buffer_size;
91 for (i = 0; i < component->output_num; i++)
92 {
93 component->output[i]->buffer_num = buffer_num;
94 component->output[i]->buffer_size = buffer_num;
95 }
96#endif
97
98 MMAL_PARAM_UNUSED(cb);
99 if (port->buffer_size)
100 if (port->type == MMAL_PORT_TYPE_OUTPUT)
101 port->component->priv->module->enabled_flags |= (1<<port->index);
102 return MMAL_SUCCESS;
103}
104
105/** Flush a port */
106static MMAL_STATUS_T splitter_port_flush(MMAL_PORT_T *port)
107{
108 MMAL_PORT_MODULE_T *port_module = port->priv->module;
109 MMAL_BUFFER_HEADER_T *buffer;
110
111 /* Flush buffers that our component is holding on to */
112 buffer = mmal_queue_get(port_module->queue);
113 while(buffer)
114 {
115 mmal_port_buffer_header_callback(port, buffer);
116 buffer = mmal_queue_get(port_module->queue);
117 }
118
119 if (port->type == MMAL_PORT_TYPE_INPUT)
120 port->component->priv->module->sent_flags = 0;
121
122 return MMAL_SUCCESS;
123}
124
125/** Disable processing on a port */
126static MMAL_STATUS_T splitter_port_disable(MMAL_PORT_T *port)
127{
128 if (port->type == MMAL_PORT_TYPE_OUTPUT)
129 port->component->priv->module->enabled_flags &= ~(1<<port->index);
130
131 /* We just need to flush our internal queue */
132 return splitter_port_flush(port);
133}
134
135/** Send a buffer header to a port */
136static MMAL_STATUS_T splitter_send_output(MMAL_BUFFER_HEADER_T *buffer, MMAL_PORT_T *out_port)
137{
138 MMAL_BUFFER_HEADER_T *out;
139 MMAL_STATUS_T status;
140
141 /* Get a buffer header from output port */
142 out = mmal_queue_get(out_port->priv->module->queue);
143 if (!out)
144 return MMAL_EAGAIN;
145
146 /* Copy our input buffer header */
147 status = mmal_buffer_header_replicate(out, buffer);
148 if (status != MMAL_SUCCESS)
149 goto error;
150
151 /* Send buffer back */
152 mmal_port_buffer_header_callback(out_port, out);
153 return MMAL_SUCCESS;
154
155 error:
156 mmal_queue_put_back(out_port->priv->module->queue, out);
157 return status;
158}
159
160/** Send a buffer header to a port */
161static MMAL_STATUS_T splitter_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
162{
163 MMAL_COMPONENT_T *component = port->component;
164 MMAL_COMPONENT_MODULE_T *module = component->priv->module;
165 MMAL_PORT_T *in_port, *out_port;
166 MMAL_BUFFER_HEADER_T *in;
167 MMAL_STATUS_T status;
168 unsigned int i;
169
170 mmal_queue_put(port->priv->module->queue, buffer);
171
172 if (module->error)
173 return MMAL_SUCCESS; /* Just do nothing */
174
175 /* Get input buffer header */
176 in_port = component->input[0];
177 in = mmal_queue_get(in_port->priv->module->queue);
178 if (!in)
179 return MMAL_SUCCESS; /* Nothing to do */
180
181 for (i = 0; i < component->output_num; i++)
182 {
183 out_port = component->output[i];
184 status = splitter_send_output(in, out_port);
185
186 if (status != MMAL_SUCCESS && status != MMAL_EAGAIN)
187 goto error;
188
189 if (status == MMAL_SUCCESS)
190 module->sent_flags |= (1<<i);
191 }
192
193 /* Check if we're done with the input buffer */
194 if ((module->sent_flags & module->enabled_flags) == module->enabled_flags)
195 {
196 in->length = 0; /* Consume the input buffer */
197 mmal_port_buffer_header_callback(in_port, in);
198 module->sent_flags = 0;
199 return MMAL_SUCCESS;
200 }
201
202 /* We're not done yet so put the buffer back in the queue */
203 mmal_queue_put(in_port->priv->module->queue, in);
204 return MMAL_SUCCESS;
205
206 error:
207 mmal_queue_put(in_port->priv->module->queue, in);
208
209 status = mmal_event_error_send(port->component, status);
210 if (status != MMAL_SUCCESS)
211 {
212 LOG_ERROR("unable to send an error event buffer (%i)", (int)status);
213 return MMAL_SUCCESS;
214 }
215 module->error = 1;
216 return MMAL_SUCCESS;
217}
218
219/** Set format on a port */
220static MMAL_STATUS_T splitter_port_format_commit(MMAL_PORT_T *port)
221{
222 MMAL_COMPONENT_T *component = port->component;
223 MMAL_STATUS_T status;
224 unsigned int i;
225
226 /* Sanity check */
227 if (port->type == MMAL_PORT_TYPE_OUTPUT)
228 {
229 LOG_ERROR("output port is read-only");
230 return MMAL_EINVAL;
231 }
232
233 /* Commit the format on all output ports */
234 for (i = 0; i < component->output_num; i++)
235 {
236 status = mmal_format_full_copy(component->output[i]->format, port->format);
237 if (status != MMAL_SUCCESS)
238 return status;
239 }
240
241 return MMAL_SUCCESS;
242}
243
244static MMAL_STATUS_T splitter_port_parameter_set(MMAL_PORT_T *port, const MMAL_PARAMETER_HEADER_T *param)
245{
246 MMAL_COMPONENT_T *component = port->component;
247 unsigned int i;
248
249 switch (param->id)
250 {
251 case MMAL_PARAMETER_BUFFER_REQUIREMENTS:
252 {
253 /* Propagate the requirements to all the ports */
254 const MMAL_PARAMETER_BUFFER_REQUIREMENTS_T *req = (const MMAL_PARAMETER_BUFFER_REQUIREMENTS_T *)param;
255 uint32_t buffer_num_min = MMAL_MAX(port->buffer_num_min, req->buffer_num_min);
256 uint32_t buffer_num_recommended = MMAL_MAX(port->buffer_num_recommended, req->buffer_num_recommended);
257 uint32_t buffer_size_min = MMAL_MAX(port->buffer_size_min, req->buffer_size_min);
258 uint32_t buffer_size_recommended = MMAL_MAX(port->buffer_size_recommended, req->buffer_size_recommended);
259
260 component->input[0]->buffer_num_min = buffer_num_min;
261 component->input[0]->buffer_num_recommended = buffer_num_recommended;
262 component->input[0]->buffer_size_min = buffer_size_min;
263 component->input[0]->buffer_size_recommended = buffer_size_recommended;
264 for (i = 0; i < component->output_num; i++)
265 {
266 component->output[i]->buffer_num_min = buffer_num_min;
267 component->output[i]->buffer_num_recommended = buffer_num_recommended;
268 component->output[i]->buffer_size_min = buffer_size_min;
269 component->output[i]->buffer_size_recommended = buffer_size_recommended;
270 }
271
272 }
273 return MMAL_SUCCESS;
274
275 default:
276 return MMAL_ENOSYS;
277 }
278}
279
280/** Create an instance of a component */
281static MMAL_STATUS_T mmal_component_create_splitter(const char *name, MMAL_COMPONENT_T *component)
282{
283 MMAL_COMPONENT_MODULE_T *module;
284 MMAL_STATUS_T status = MMAL_ENOMEM;
285 unsigned int i;
286 MMAL_PARAM_UNUSED(name);
287
288 /* Allocate the context for our module */
289 component->priv->module = module = vcos_malloc(sizeof(*module), "mmal module");
290 if (!module)
291 return MMAL_ENOMEM;
292 memset(module, 0, sizeof(*module));
293
294 component->priv->pf_destroy = splitter_component_destroy;
295
296 /* Allocate and initialise all the ports for this component */
297 component->input = mmal_ports_alloc(component, 1, MMAL_PORT_TYPE_INPUT, sizeof(MMAL_PORT_MODULE_T));
298 if(!component->input)
299 goto error;
300 component->input_num = 1;
301 component->input[0]->priv->pf_enable = splitter_port_enable;
302 component->input[0]->priv->pf_disable = splitter_port_disable;
303 component->input[0]->priv->pf_flush = splitter_port_flush;
304 component->input[0]->priv->pf_send = splitter_port_send;
305 component->input[0]->priv->pf_set_format = splitter_port_format_commit;
306 component->input[0]->priv->pf_parameter_set = splitter_port_parameter_set;
307 component->input[0]->buffer_num_min = 1;
308 component->input[0]->buffer_num_recommended = 0;
309 component->input[0]->priv->module->queue = mmal_queue_create();
310 if(!component->input[0]->priv->module->queue)
311 goto error;
312
313 component->output = mmal_ports_alloc(component, SPLITTER_OUTPUT_PORTS_NUM,
314 MMAL_PORT_TYPE_OUTPUT, sizeof(MMAL_PORT_MODULE_T));
315 if(!component->output)
316 goto error;
317 component->output_num = SPLITTER_OUTPUT_PORTS_NUM;
318 for(i = 0; i < component->output_num; i++)
319 {
320 component->output[i]->priv->pf_enable = splitter_port_enable;
321 component->output[i]->priv->pf_disable = splitter_port_disable;
322 component->output[i]->priv->pf_flush = splitter_port_flush;
323 component->output[i]->priv->pf_send = splitter_port_send;
324 component->output[i]->priv->pf_set_format = splitter_port_format_commit;
325 component->output[i]->priv->pf_parameter_set = splitter_port_parameter_set;
326 component->output[i]->buffer_num_min = 1;
327 component->output[i]->buffer_num_recommended = 0;
328 component->output[i]->capabilities = MMAL_PORT_CAPABILITY_PASSTHROUGH;
329 component->output[i]->priv->module->queue = mmal_queue_create();
330 if(!component->output[i]->priv->module->queue)
331 goto error;
332 }
333
334 return MMAL_SUCCESS;
335
336 error:
337 splitter_component_destroy(component);
338 return status;
339}
340
341MMAL_CONSTRUCTOR(mmal_register_component_splitter);
342void mmal_register_component_splitter(void)
343{
344 mmal_component_supplier_register("splitter", mmal_component_create_splitter);
345}
346