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/*****************************************************************************/
34typedef struct MMAL_COMPONENT_MODULE_T
35{
36 MMAL_STATUS_T status; /**< current status of the component */
37
38} MMAL_COMPONENT_MODULE_T;
39
40typedef struct MMAL_PORT_MODULE_T
41{
42 MMAL_QUEUE_T *queue; /**< queue for the buffers sent to the ports */
43 MMAL_BOOL_T needs_configuring; /**< port is waiting for a format commit */
44
45} MMAL_PORT_MODULE_T;
46
47/*****************************************************************************/
48
49/** Actual processing function */
50static MMAL_BOOL_T copy_do_processing(MMAL_COMPONENT_T *component)
51{
52 MMAL_COMPONENT_MODULE_T *module = component->priv->module;
53 MMAL_PORT_T *port_in = component->input[0];
54 MMAL_PORT_T *port_out = component->output[0];
55 MMAL_BUFFER_HEADER_T *in, *out;
56
57 if (port_out->priv->module->needs_configuring)
58 return 0;
59
60 in = mmal_queue_get(port_in->priv->module->queue);
61 if (!in)
62 return 0;
63
64 /* Handle event buffers */
65 if (in->cmd)
66 {
67 MMAL_EVENT_FORMAT_CHANGED_T *event = mmal_event_format_changed_get(in);
68 if (event)
69 {
70 module->status = mmal_format_full_copy(port_in->format, event->format);
71 if (module->status == MMAL_SUCCESS)
72 module->status = port_in->priv->pf_set_format(port_in);
73 if (module->status != MMAL_SUCCESS)
74 {
75 LOG_ERROR("format not set on port %s %p (%i)", port_in->name, port_in, module->status);
76 if (mmal_event_error_send(component, module->status) != MMAL_SUCCESS)
77 LOG_ERROR("unable to send an error event buffer");
78 }
79 }
80 else
81 {
82 LOG_ERROR("discarding event %i on port %s %p", (int)in->cmd, port_in->name, port_in);
83 }
84
85 in->length = 0;
86 mmal_port_buffer_header_callback(port_in, in);
87 return 1;
88 }
89
90 /* Don't do anything if we've already seen an error */
91 if (module->status != MMAL_SUCCESS)
92 {
93 mmal_queue_put_back(port_in->priv->module->queue, in);
94 return 0;
95 }
96
97 out = mmal_queue_get(port_out->priv->module->queue);
98 if (!out)
99 {
100 mmal_queue_put_back(port_in->priv->module->queue, in);
101 return 0;
102 }
103
104 /* Sanity check the output buffer is big enough */
105 if (out->alloc_size < in->length)
106 {
107 module->status = MMAL_EINVAL;
108 if (mmal_event_error_send(component, module->status) != MMAL_SUCCESS)
109 LOG_ERROR("unable to send an error event buffer");
110 return 0;
111 }
112
113 mmal_buffer_header_mem_lock(out);
114 mmal_buffer_header_mem_lock(in);
115 memcpy(out->data, in->data + in->offset, in->length);
116 mmal_buffer_header_mem_unlock(in);
117 mmal_buffer_header_mem_unlock(out);
118 out->length = in->length;
119 out->offset = 0;
120 out->flags = in->flags;
121 out->pts = in->pts;
122 out->dts = in->dts;
123 *out->type = *in->type;
124
125 /* Send buffers back */
126 in->length = 0;
127 mmal_port_buffer_header_callback(port_in, in);
128 mmal_port_buffer_header_callback(port_out, out);
129 return 1;
130}
131
132/*****************************************************************************/
133static void copy_do_processing_loop(MMAL_COMPONENT_T *component)
134{
135 while (copy_do_processing(component));
136}
137
138/** Destroy a previously created component */
139static MMAL_STATUS_T copy_component_destroy(MMAL_COMPONENT_T *component)
140{
141 unsigned int i;
142
143 for(i = 0; i < component->input_num; i++)
144 if(component->input[i]->priv->module->queue)
145 mmal_queue_destroy(component->input[i]->priv->module->queue);
146 if(component->input_num)
147 mmal_ports_free(component->input, component->input_num);
148
149 for(i = 0; i < component->output_num; i++)
150 if(component->output[i]->priv->module->queue)
151 mmal_queue_destroy(component->output[i]->priv->module->queue);
152 if(component->output_num)
153 mmal_ports_free(component->output, component->output_num);
154
155 vcos_free(component->priv->module);
156 return MMAL_SUCCESS;
157}
158
159/** Enable processing on a port */
160static MMAL_STATUS_T copy_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
161{
162 MMAL_PARAM_UNUSED(cb);
163
164 /* We need to propagate the buffer requirements when the input port is
165 * enabled */
166 if (port->type == MMAL_PORT_TYPE_INPUT)
167 return port->priv->pf_set_format(port);
168
169 return MMAL_SUCCESS;
170}
171
172/** Flush a port */
173static MMAL_STATUS_T copy_port_flush(MMAL_PORT_T *port)
174{
175 MMAL_PORT_MODULE_T *port_module = port->priv->module;
176 MMAL_BUFFER_HEADER_T *buffer;
177
178 /* Flush buffers that our component is holding on to */
179 buffer = mmal_queue_get(port_module->queue);
180 while(buffer)
181 {
182 mmal_port_buffer_header_callback(port, buffer);
183 buffer = mmal_queue_get(port_module->queue);
184 }
185
186 return MMAL_SUCCESS;
187}
188
189/** Disable processing on a port */
190static MMAL_STATUS_T copy_port_disable(MMAL_PORT_T *port)
191{
192 /* We just need to flush our internal queue */
193 return copy_port_flush(port);
194}
195
196/** Send a buffer header to a port */
197static MMAL_STATUS_T copy_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
198{
199 mmal_queue_put(port->priv->module->queue, buffer);
200 mmal_component_action_trigger(port->component);
201 return MMAL_SUCCESS;
202}
203
204/** Set format on input port */
205static MMAL_STATUS_T copy_input_port_format_commit(MMAL_PORT_T *in)
206{
207 MMAL_COMPONENT_T *component = in->component;
208 MMAL_PORT_T *out = component->output[0];
209 MMAL_EVENT_FORMAT_CHANGED_T *event;
210 MMAL_BUFFER_HEADER_T *buffer;
211 MMAL_STATUS_T status;
212
213 /* Check if there's anything to propagate to the output port */
214 /* The format of the output port needs to match the input port */
215 if (!mmal_format_compare(in->format, out->format) &&
216 out->buffer_size_min == out->buffer_size_recommended &&
217 out->buffer_size_min == MMAL_MAX(in->buffer_size_min, in->buffer_size))
218 return MMAL_SUCCESS;
219
220 /* If the output port is not enabled we just need to update its format.
221 * Otherwise we'll have to trigger a format changed event for it. */
222 if (!out->is_enabled)
223 {
224 out->buffer_size_min = out->buffer_size_recommended =
225 MMAL_MAX(in->buffer_size, in->buffer_size_min);
226 return mmal_format_full_copy(out->format, in->format);
227 }
228
229 /* Send an event on the output port */
230 status = mmal_port_event_get(out, &buffer, MMAL_EVENT_FORMAT_CHANGED);
231 if (status != MMAL_SUCCESS)
232 {
233 LOG_ERROR("unable to get an event buffer");
234 return status;
235 }
236
237 event = mmal_event_format_changed_get(buffer);
238 mmal_format_copy(event->format, in->format); /* FIXME: can full copy be done ? */
239
240 /* Pass on the buffer requirements */
241 event->buffer_num_min = out->buffer_num_min;
242 event->buffer_num_recommended = out->buffer_num_recommended;
243 event->buffer_size_min = event->buffer_size_recommended =
244 MMAL_MAX(in->buffer_size_min, in->buffer_size);
245
246 out->priv->module->needs_configuring = 1;
247 mmal_port_event_send(out, buffer);
248 return status;
249}
250
251/** Set format on output port */
252static MMAL_STATUS_T copy_output_port_format_commit(MMAL_PORT_T *out)
253{
254 MMAL_COMPONENT_T *component = out->component;
255 MMAL_PORT_T *in = component->input[0];
256
257 /* The format of the output port needs to match the input port */
258 if (mmal_format_compare(out->format, in->format))
259 return MMAL_EINVAL;
260
261 out->priv->module->needs_configuring = 0;
262 mmal_component_action_trigger(out->component);
263 return MMAL_SUCCESS;
264}
265
266/** Create an instance of a component */
267static MMAL_STATUS_T mmal_component_create_copy(const char *name, MMAL_COMPONENT_T *component)
268{
269 MMAL_COMPONENT_MODULE_T *module;
270 MMAL_STATUS_T status = MMAL_ENOMEM;
271 MMAL_PARAM_UNUSED(name);
272
273 /* Allocate the context for our module */
274 component->priv->module = module = vcos_malloc(sizeof(*module), "mmal module");
275 if (!module)
276 return MMAL_ENOMEM;
277 memset(module, 0, sizeof(*module));
278
279 component->priv->pf_destroy = copy_component_destroy;
280
281 /* Allocate and initialise all the ports for this component */
282 component->input = mmal_ports_alloc(component, 1, MMAL_PORT_TYPE_INPUT, sizeof(MMAL_PORT_MODULE_T));
283 if(!component->input)
284 goto error;
285 component->input_num = 1;
286 component->input[0]->priv->pf_enable = copy_port_enable;
287 component->input[0]->priv->pf_disable = copy_port_disable;
288 component->input[0]->priv->pf_flush = copy_port_flush;
289 component->input[0]->priv->pf_send = copy_port_send;
290 component->input[0]->priv->pf_set_format = copy_input_port_format_commit;
291 component->input[0]->buffer_num_min = 1;
292 component->input[0]->buffer_num_recommended = 0;
293 component->input[0]->priv->module->queue = mmal_queue_create();
294 if(!component->input[0]->priv->module->queue)
295 goto error;
296
297 component->output = mmal_ports_alloc(component, 1, MMAL_PORT_TYPE_OUTPUT, sizeof(MMAL_PORT_MODULE_T));
298 if(!component->output)
299 goto error;
300 component->output_num = 1;
301 component->output[0]->priv->pf_enable = copy_port_enable;
302 component->output[0]->priv->pf_disable = copy_port_disable;
303 component->output[0]->priv->pf_flush = copy_port_flush;
304 component->output[0]->priv->pf_send = copy_port_send;
305 component->output[0]->priv->pf_set_format = copy_output_port_format_commit;
306 component->output[0]->buffer_num_min = 1;
307 component->output[0]->buffer_num_recommended = 0;
308 component->output[0]->priv->module->queue = mmal_queue_create();
309 if(!component->output[0]->priv->module->queue)
310 goto error;
311
312 status = mmal_component_action_register(component, copy_do_processing_loop);
313 if (status != MMAL_SUCCESS)
314 goto error;
315
316 return MMAL_SUCCESS;
317
318 error:
319 copy_component_destroy(component);
320 return status;
321}
322
323MMAL_CONSTRUCTOR(mmal_register_component_copy);
324void mmal_register_component_copy(void)
325{
326 mmal_component_supplier_register("copy", mmal_component_create_copy);
327}
328