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 "mmal_logging.h"
30#include "core/mmal_port_private.h"
31#include "core/mmal_component_private.h"
32#include "core/mmal_clock_private.h"
33
34#define SCHEDULER_CLOCK_PORTS_NUM 1
35#define SCHEDULER_INPUT_PORTS_NUM 1
36#define SCHEDULER_OUTPUT_PORTS_NUM 1
37
38#define SCHEDULER_REQUEST_SLOTS 16
39
40/*****************************************************************************/
41typedef struct MMAL_COMPONENT_MODULE_T
42{
43 MMAL_STATUS_T status; /**< current status of the component */
44} MMAL_COMPONENT_MODULE_T;
45
46
47typedef struct MMAL_PORT_MODULE_T
48{
49 MMAL_QUEUE_T *queue; /**< queue for the buffers sent to the port */
50 int64_t last_ts; /***< Last timestamp seen on the input port */
51} MMAL_PORT_MODULE_T;
52
53
54/*****************************************************************************/
55/** Process an event buffer */
56static MMAL_STATUS_T scheduler_event_process(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
57{
58 MMAL_STATUS_T status = MMAL_EINVAL;
59
60 if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED)
61 {
62 MMAL_EVENT_FORMAT_CHANGED_T *event =
63 mmal_event_format_changed_get(buffer);
64 if (!event)
65 goto end;
66
67 status = mmal_format_full_copy(port->format, event->format);
68 if (status == MMAL_SUCCESS)
69 status = mmal_port_format_commit(port);
70 if (status != MMAL_SUCCESS)
71 {
72 LOG_ERROR("format commit failed on port %s (%i)",
73 port->name, status);
74 goto end;
75 }
76
77 status = MMAL_SUCCESS;
78 }
79 /* Forward any other event as is to the next component */
80 else
81 {
82 LOG_DEBUG("forwarding unknown event %4.4s", (char *)&buffer->cmd);
83 status = mmal_event_forward(buffer, port->component->output[port->index]);
84 if (status != MMAL_SUCCESS)
85 {
86 LOG_ERROR("unable to forward event %4.4s", (char *)&buffer->cmd);
87 goto end;
88 }
89 }
90
91 end:
92 buffer->length = 0;
93 mmal_port_buffer_header_callback(port, buffer);
94 return status;
95}
96
97/** Invoked when a clock request has been serviced */
98static void scheduler_component_clock_port_request_cb(MMAL_PORT_T *port, int64_t media_time, void *cb_data)
99{
100 MMAL_COMPONENT_T *component = port->component;;
101 MMAL_PORT_T *port_in = component->input[0];
102 MMAL_PORT_T *port_out = component->output[0];
103 MMAL_BUFFER_HEADER_T *buffer = (MMAL_BUFFER_HEADER_T*)cb_data;
104
105 LOG_TRACE("media-time %"PRIi64" pts %"PRIi64" delta %"PRIi64,
106 media_time, buffer->pts, media_time - buffer->pts);
107
108 if (buffer->cmd)
109 scheduler_event_process(port_in, buffer);
110 else
111 /* Forward the buffer to the next component */
112 mmal_port_buffer_header_callback(port_out, buffer);
113}
114
115/** Process buffers on the input and output ports */
116static MMAL_BOOL_T scheduler_component_process_buffers(MMAL_COMPONENT_T *component)
117{
118 MMAL_COMPONENT_MODULE_T *module = component->priv->module;
119 MMAL_PORT_T *port_in = component->input[0];
120 MMAL_PORT_T *port_out = component->output[0];
121 MMAL_QUEUE_T *queue_in = port_in->priv->module->queue;
122 MMAL_QUEUE_T *queue_out = port_out->priv->module->queue;
123 MMAL_BUFFER_HEADER_T *in, *out;
124 MMAL_STATUS_T cb_status = MMAL_EINVAL;
125
126 /* Don't do anything if we've already seen an error */
127 if (module->status != MMAL_SUCCESS)
128 {
129 LOG_ERROR("module failure");
130 return MMAL_FALSE;
131 }
132
133 in = mmal_queue_get(queue_in);
134
135 /* Special case for dealing with event buffers */
136 if (in && in->cmd)
137 {
138 /* We normally schedule cmds so they come out in the right order,
139 * except when we don't know when to schedule them, which will only
140 * happen at the start of the stream.
141 * The fudge factor added to the last timestamp here is because the
142 * cmd really applies to the next buffer so we want to make sure
143 * we leave enough time to the next component to process the previous
144 * buffer before forwarding the event. */
145 in->pts = port_in->priv->module->last_ts + 1000;
146 if (in->pts != MMAL_TIME_UNKNOWN)
147 cb_status = mmal_port_clock_request_add(component->clock[0],
148 in->pts, scheduler_component_clock_port_request_cb, in);
149 if (cb_status != MMAL_SUCCESS)
150 {
151 if (in->pts != MMAL_TIME_UNKNOWN)
152 LOG_ERROR("failed to add request for cmd");
153 scheduler_event_process(port_in, in);
154 }
155 return MMAL_TRUE;
156 }
157
158 /* Need both an input and output buffer to be able to go any further */
159 out = mmal_queue_get(queue_out);
160 if (!in || !out)
161 goto end;
162
163 if (port_out->capabilities & MMAL_PORT_CAPABILITY_PASSTHROUGH)
164 {
165 /* Just need to keep a reference to the input buffer */
166 module->status = mmal_buffer_header_replicate(out, in);
167 }
168 else
169 {
170 /* Make a full copy of the input payload */
171 if (out->alloc_size < in->length)
172 {
173 LOG_ERROR("output buffer too small");
174
175 module->status = MMAL_EINVAL;
176 if (mmal_event_error_send(component, module->status) != MMAL_SUCCESS)
177 LOG_ERROR("unable to send an error event buffer");
178 goto end;
179 }
180 mmal_buffer_header_mem_lock(out);
181 mmal_buffer_header_mem_lock(in);
182 memcpy(out->data, in->data + in->offset, in->length);
183 mmal_buffer_header_mem_unlock(in);
184 mmal_buffer_header_mem_unlock(out);
185 out->length = in->length;
186 out->offset = 0;
187 out->flags = in->flags;
188 out->pts = in->pts;
189 out->dts = in->dts;
190 *out->type = *in->type;
191 }
192
193 /* Finished with the input buffer, so return it */
194 in->length = 0;
195 mmal_port_buffer_header_callback(port_in, in);
196 in = 0;
197
198 if (module->status != MMAL_SUCCESS)
199 {
200 LOG_ERROR("failed to replicate buffer");
201 goto end;
202 }
203
204 /* Request a clock callback when media-time >= pts */
205 LOG_TRACE("requesting callback at %"PRIi64,out->pts);
206 port_in->priv->module->last_ts = out->pts;
207
208 cb_status = mmal_port_clock_request_add(component->clock[0], out->pts,
209 scheduler_component_clock_port_request_cb, out);
210 if (cb_status != MMAL_SUCCESS)
211 {
212 LOG_ERROR("failed to add request");
213 out->length = 0;
214 mmal_port_buffer_header_callback(port_out, out);
215 if (cb_status != MMAL_ECORRUPT)
216 module->status = cb_status;
217 }
218 out = 0;
219
220 end:
221 if (in)
222 mmal_queue_put_back(queue_in, in);
223 if (out)
224 mmal_queue_put_back(queue_out, out);
225
226 return mmal_queue_length(queue_in) && mmal_queue_length(queue_out);
227}
228
229/** Main processing action */
230static void scheduler_component_action(MMAL_COMPONENT_T *component)
231{
232 /* Send requests to the clock */
233 while (scheduler_component_process_buffers(component));
234}
235
236/** Destroy a scheduler component */
237static MMAL_STATUS_T scheduler_component_destroy(MMAL_COMPONENT_T *component)
238{
239 unsigned int i;
240
241 for (i = 0; i < component->input_num; i++)
242 if (component->input[i]->priv->module->queue)
243 mmal_queue_destroy(component->input[i]->priv->module->queue);
244 if (component->input_num)
245 mmal_ports_free(component->input, component->input_num);
246
247 for (i = 0; i < component->output_num; i++)
248 if (component->output[i]->priv->module->queue)
249 mmal_queue_destroy(component->output[i]->priv->module->queue);
250 if (component->output_num)
251 mmal_ports_free(component->output, component->output_num);
252
253 if (component->clock_num)
254 mmal_ports_clock_free(component->clock, component->clock_num);
255
256 vcos_free(component->priv->module);
257 return MMAL_SUCCESS;
258}
259
260/** Enable processing on a port */
261static MMAL_STATUS_T scheduler_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
262{
263 MMAL_PARAM_UNUSED(port);
264 MMAL_PARAM_UNUSED(cb);
265 return MMAL_SUCCESS;
266}
267
268/** Flush a port */
269static MMAL_STATUS_T scheduler_port_flush(MMAL_PORT_T *port)
270{
271 MMAL_PORT_MODULE_T *port_module = port->priv->module;
272 MMAL_BUFFER_HEADER_T *buffer;
273
274 /* Flush buffers associated with pending clock requests */
275 mmal_port_clock_request_flush(port->component->clock[0]);
276
277 /* Flush buffers that our component is holding on to */
278 buffer = mmal_queue_get(port_module->queue);
279 while (buffer)
280 {
281 mmal_port_buffer_header_callback(port, buffer);
282 buffer = mmal_queue_get(port_module->queue);
283 }
284
285 port->priv->module->last_ts = MMAL_TIME_UNKNOWN;
286 return MMAL_SUCCESS;
287}
288
289/** Disable processing on a port */
290static MMAL_STATUS_T scheduler_port_disable(MMAL_PORT_T *port)
291{
292 /* We just need to flush our internal queue */
293 return scheduler_port_flush(port);
294}
295
296/** Send a buffer header to a port */
297static MMAL_STATUS_T scheduler_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
298{
299 MMAL_COMPONENT_T *component = port->component;
300
301 /* notify the clock port */
302 if (port->type == MMAL_PORT_TYPE_INPUT && !buffer->cmd)
303 {
304 MMAL_CLOCK_BUFFER_INFO_T info = { buffer->pts, vcos_getmicrosecs() };
305 mmal_port_clock_input_buffer_info(port->component->clock[0], &info);
306 }
307
308 mmal_queue_put(port->priv->module->queue, buffer);
309 return mmal_component_action_trigger(component);
310}
311
312/** Set format on an input port */
313static MMAL_STATUS_T scheduler_input_port_format_commit(MMAL_PORT_T *port)
314{
315 MMAL_COMPONENT_T *component = port->component;
316 MMAL_EVENT_FORMAT_CHANGED_T *event;
317 MMAL_PORT_T *output = component->output[0];
318 MMAL_BUFFER_HEADER_T *buffer;
319 MMAL_STATUS_T status;
320
321 /* If the output port is not enabled we just need to update its format.
322 * Otherwise we'll have to trigger a format changed event for it. */
323 if (!output->is_enabled)
324 {
325 status = mmal_format_full_copy(output->format, port->format);
326 return status;
327 }
328
329 /* Send an event on the output port */
330 status = mmal_port_event_get(output, &buffer, MMAL_EVENT_FORMAT_CHANGED);
331 if (status != MMAL_SUCCESS)
332 {
333 LOG_ERROR("unable to get an event buffer");
334 return status;
335 }
336 event = mmal_event_format_changed_get(buffer);
337 if (!event)
338 {
339 mmal_buffer_header_release(buffer);
340 LOG_ERROR("failed to set format");
341 return MMAL_EINVAL;
342 }
343 mmal_format_copy(event->format, port->format);
344
345 /* Pass on the buffer requirements */
346 event->buffer_num_min = port->buffer_num_min;
347 event->buffer_size_min = port->buffer_size_min;
348 event->buffer_num_recommended = port->buffer_num_recommended;
349 event->buffer_size_recommended = port->buffer_size_recommended;
350
351 mmal_port_event_send(component->output[port->index], buffer);
352 return status;
353}
354
355/** Set format on an output port */
356static MMAL_STATUS_T scheduler_output_port_format_commit(MMAL_PORT_T *port)
357{
358 /* The format of the output port needs to match the input port */
359 if (mmal_format_compare(port->format, port->component->input[port->index]->format))
360 LOG_DEBUG("output port format different from input port");
361
362 return MMAL_SUCCESS;
363}
364
365static MMAL_STATUS_T scheduler_port_parameter_set(MMAL_PORT_T *port, const MMAL_PARAMETER_HEADER_T *param)
366{
367 MMAL_COMPONENT_T *component = port->component;
368 MMAL_PORT_T *in = component->input[port->index], *out = component->input[port->index];
369
370 switch (param->id)
371 {
372 case MMAL_PARAMETER_BUFFER_REQUIREMENTS:
373 {
374 /* Propagate the requirements to the matching input and output the ports */
375 const MMAL_PARAMETER_BUFFER_REQUIREMENTS_T *req = (const MMAL_PARAMETER_BUFFER_REQUIREMENTS_T *)param;
376 uint32_t buffer_num_min = MMAL_MAX(port->buffer_num_min, req->buffer_num_min);
377 uint32_t buffer_num_recommended = MMAL_MAX(port->buffer_num_recommended, req->buffer_num_recommended);
378 uint32_t buffer_size_min = MMAL_MAX(port->buffer_size_min, req->buffer_size_min);
379 uint32_t buffer_size_recommended = MMAL_MAX(port->buffer_size_recommended, req->buffer_size_recommended);
380
381 in->buffer_num_min = buffer_num_min;
382 in->buffer_num_recommended = buffer_num_recommended;
383 in->buffer_size_min = buffer_size_min;
384 in->buffer_size_recommended = buffer_size_recommended;
385 out->buffer_num_min = buffer_num_min;
386 out->buffer_num_recommended = buffer_num_recommended;
387 out->buffer_size_min = buffer_size_min;
388 out->buffer_size_recommended = buffer_size_recommended;
389 }
390 return MMAL_SUCCESS;
391
392 default:
393 return MMAL_ENOSYS;
394 }
395}
396
397/** Create an instance of a component */
398static MMAL_STATUS_T mmal_component_create_scheduler(const char *name, MMAL_COMPONENT_T *component)
399{
400 MMAL_COMPONENT_MODULE_T *module;
401 MMAL_STATUS_T status = MMAL_ENOMEM;
402 int disable_passthrough = 0;
403 unsigned int i;
404
405 /* Allocate the context for our module */
406 component->priv->module = module = vcos_calloc(1, sizeof(*module), "mmal module");
407 if (!module)
408 return MMAL_ENOMEM;
409
410 component->priv->pf_destroy = scheduler_component_destroy;
411
412 /* Allocate and initialise all the ports for this component */
413 component->input = mmal_ports_alloc(component, SCHEDULER_INPUT_PORTS_NUM,
414 MMAL_PORT_TYPE_INPUT, sizeof(MMAL_PORT_MODULE_T));
415 if (!component->input)
416 goto error;
417 component->input_num = SCHEDULER_INPUT_PORTS_NUM;
418 for (i = 0; i < component->input_num; i++)
419 {
420 component->input[i]->priv->pf_enable = scheduler_port_enable;
421 component->input[i]->priv->pf_disable = scheduler_port_disable;
422 component->input[i]->priv->pf_flush = scheduler_port_flush;
423 component->input[i]->priv->pf_send = scheduler_port_send;
424 component->input[i]->priv->pf_set_format = scheduler_input_port_format_commit;
425 component->input[i]->priv->pf_parameter_set = scheduler_port_parameter_set;
426 component->input[i]->buffer_num_min = 1;
427 component->input[i]->buffer_num_recommended = 0;
428 component->input[i]->capabilities = MMAL_PORT_CAPABILITY_SUPPORTS_EVENT_FORMAT_CHANGE;
429 component->input[i]->priv->module->queue = mmal_queue_create();
430 if (!component->input[i]->priv->module->queue)
431 goto error;
432 component->input[i]->priv->module->last_ts = MMAL_TIME_UNKNOWN;
433 }
434
435 /* Override passthrough behaviour */
436 if (strstr(name, ".copy"))
437 {
438 LOG_TRACE("disable passthrough on output ports");
439 disable_passthrough = 1;
440 }
441
442 component->output = mmal_ports_alloc(component, SCHEDULER_OUTPUT_PORTS_NUM,
443 MMAL_PORT_TYPE_OUTPUT, sizeof(MMAL_PORT_MODULE_T));
444 if (!component->output)
445 goto error;
446 component->output_num = SCHEDULER_OUTPUT_PORTS_NUM;
447 for (i = 0; i < component->output_num; i++)
448 {
449 component->output[i]->priv->pf_enable = scheduler_port_enable;
450 component->output[i]->priv->pf_disable = scheduler_port_disable;
451 component->output[i]->priv->pf_flush = scheduler_port_flush;
452 component->output[i]->priv->pf_send = scheduler_port_send;
453 component->output[i]->priv->pf_set_format = scheduler_output_port_format_commit;
454 component->output[i]->priv->pf_parameter_set = scheduler_port_parameter_set;
455 component->output[i]->buffer_num_min = 1;
456 component->output[i]->buffer_num_recommended = 0;
457 component->output[i]->capabilities = disable_passthrough ? 0 : MMAL_PORT_CAPABILITY_PASSTHROUGH;
458 component->output[i]->priv->module->queue = mmal_queue_create();
459 if (!component->output[i]->priv->module->queue)
460 goto error;
461 }
462
463 /* Create the clock port (clock ports are managed by the framework) */
464 component->clock = mmal_ports_clock_alloc(component, SCHEDULER_CLOCK_PORTS_NUM, 0, NULL);
465 if (!component->clock)
466 goto error;
467 component->clock_num = SCHEDULER_CLOCK_PORTS_NUM;
468
469 status = mmal_component_action_register(component, scheduler_component_action);
470 if (status != MMAL_SUCCESS)
471 goto error;
472
473 return MMAL_SUCCESS;
474
475error:
476 scheduler_component_destroy(component);
477 return status;
478}
479
480MMAL_CONSTRUCTOR(mmal_register_component_scheduler);
481void mmal_register_component_scheduler(void)
482{
483 mmal_component_supplier_register("scheduler", mmal_component_create_scheduler);
484}
485
486