Änderungen Gruppe 30_10_2022 V2

This commit is contained in:
2022-10-30 20:01:47 +01:00
parent 269f807b31
commit 2517e52e59
239 changed files with 54983 additions and 9 deletions
@@ -0,0 +1,34 @@
add_library(mmal_components ${LIBRARY_TYPE}
container_reader.c
null_sink.c
passthrough.c
scheduler.c
splitter.c
copy.c
artificial_camera.c
aggregator.c
clock.c
spdif.c
)
set(extra_components_SRCS avcodec_video_decoder.c avcodec_audio_decoder.c
sdl_video_render.c sdl_audio_render.c aaf_audio_render.cpp android_media_codec.cpp)
#target_link_libraries(mmal_components avcodec avutil)
#target_link_libraries(mmal_components SDL)
#if (WIN32)
#target_link_libraries(mmal_components avcore avutil z) # For avcodec
#target_link_libraries(mmal_components gdi32 winmm) # For SDL
#endif (WIN32)
add_custom_target(mmal_components_extra ALL
COMMAND touch ${extra_components_SRCS}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/interface/mmal/components)
set(container_libs ${container_libs} containers)
target_link_libraries(mmal_components ${container_libs} mmal_util)
target_link_libraries(mmal_components mmal_core)
install(TARGETS mmal_components DESTINATION lib)
@@ -0,0 +1,504 @@
/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "mmal.h"
#include "mmal_logging.h"
#include "core/mmal_component_private.h"
#include "core/mmal_port_private.h"
#include "core/mmal_clock_private.h"
#include <media/AudioTrack.h>
#include <utils/Mutex.h>
using namespace android;
/* Buffering requirements */
#define INPUT_MIN_BUFFER_NUM 4
#define INPUT_RECOMMENDED_BUFFER_NUM 8
#define SPDIF_AC3_FRAME_SIZE 6144
/*****************************************************************************/
enum TRACK_STATE_T {
TRACK_STATE_STOPPED,
TRACK_STATE_RUNNING,
TRACK_STATE_PAUSED
};
typedef struct MMAL_COMPONENT_MODULE_T
{
MMAL_STATUS_T status;
MMAL_QUEUE_T *queue;
android::sp<android::AudioTrack> track;
Mutex *lock;
uint32_t bytes_queued;
MMAL_BOOL_T is_enabled;
TRACK_STATE_T state;
MMAL_ES_FORMAT_T *format; /**< format currently configured */
} MMAL_COMPONENT_MODULE_T;
/*****************************************************************************/
static void aaf_track_callback(int event, void *user, void *info);
static struct encoding_table_t {
MMAL_FOURCC_T encoding;
audio_format_t format;
} encoding_list[] =
{ {MMAL_ENCODING_PCM_SIGNED, AUDIO_FORMAT_PCM_16_BIT},
#ifdef ANDROID_SUPPORTS_AC3
{MMAL_ENCODING_AC3, AUDIO_FORMAT_AC3_SPDIF},
{MMAL_ENCODING_EAC3, AUDIO_FORMAT_EAC3_SPDIF},
#endif
{0, AUDIO_FORMAT_INVALID}
};
static audio_format_t encoding_to_audio_format(MMAL_FOURCC_T encoding)
{
struct encoding_table_t *entry = encoding_list;
for (entry = encoding_list; entry->encoding; entry++)
if (entry->encoding == encoding)
break;
return entry->format;
}
static void aaf_track_destroy(MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
if (module->track != NULL)
{
module->track->stop();
module->track = NULL;
}
}
static MMAL_STATUS_T aaf_track_create(MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_PORT_T *port = component->input[0];
audio_channel_mask_t channels_mask;
int frame_count = 256;
/* Reset track on format change. Can do better than that? */
if (module->track != NULL)
aaf_track_destroy(component);
channels_mask = audio_channel_out_mask_from_count(port->format->es->audio.channels);
LOG_INFO("%s(%p) %4.4s, %i Hz, mask %x, chan %i", port->name, port,
(char *)&port->format->encoding,
(int)port->format->es->audio.sample_rate, channels_mask,
(int)port->format->es->audio.channels);
AudioTrack::getMinFrameCount(&frame_count);
if (port->format->encoding == MMAL_ENCODING_AC3)
frame_count = SPDIF_AC3_FRAME_SIZE;
else if (port->format->encoding == MMAL_ENCODING_EAC3)
frame_count = SPDIF_AC3_FRAME_SIZE * 4;
frame_count *= 2; /* Twice the minimum should be enough */
module->track = new AudioTrack(AUDIO_STREAM_MUSIC,
port->format->es->audio.sample_rate,
encoding_to_audio_format(port->format->encoding), channels_mask,
frame_count, port->format->encoding == MMAL_ENCODING_PCM_SIGNED ?
AUDIO_OUTPUT_FLAG_NONE : AUDIO_OUTPUT_FLAG_DIRECT,
&aaf_track_callback, port, 0);
if (module->track == NULL || module->track->initCheck() != OK)
{
LOG_ERROR("%s(%p): track creation failed", port->name, port);
module->track = NULL;
return MMAL_ENOSYS;
}
return MMAL_SUCCESS;
}
static void aaf_track_callback(int event, void *user, void *info)
{
MMAL_PORT_T *port = (MMAL_PORT_T *)user;
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_BUFFER_HEADER_T *buffer;
AudioTrack::Buffer *trackbuf = NULL;
unsigned int bytes, space;
uint8_t *dest;
if (event == AudioTrack::EVENT_UNDERRUN)
LOG_ERROR("underrun");
if (event != AudioTrack::EVENT_MORE_DATA)
return;
trackbuf = (AudioTrack::Buffer *)info;
space = trackbuf->size;
dest = (uint8_t *)trackbuf->raw;
trackbuf->size = 0;
if (!mmal_queue_length(module->queue))
{
LOG_ERROR("no buffers queued");
return;
}
while (space > 0)
{
buffer = mmal_queue_get(module->queue);
if (!buffer)
break;
bytes = MMAL_MIN(buffer->length, space);
memcpy(dest, buffer->data + buffer->offset, bytes);
buffer->offset += bytes;
buffer->length -= bytes;
dest += bytes;
space -= bytes;
trackbuf->size += bytes;
if (buffer->length)
{
/* Re-queue */
mmal_queue_put_back(module->queue, buffer);
continue;
}
/* Handle the EOS */
if (buffer->flags & MMAL_BUFFER_HEADER_FLAG_EOS)
mmal_event_eos_send(port);
buffer->offset = 0;
mmal_port_buffer_header_callback(port, buffer);
}
module->lock->lock();
module->bytes_queued -= trackbuf->size;
module->lock->unlock();
}
static MMAL_STATUS_T aaf_track_state_update(MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
TRACK_STATE_T new_state = TRACK_STATE_STOPPED;
if (module->track == NULL)
return MMAL_SUCCESS;
if (module->is_enabled)
{
MMAL_RATIONAL_T scale = mmal_port_clock_scale_get(component->clock[0]);
new_state = TRACK_STATE_PAUSED;
if (scale.den && scale.den == scale.num)
new_state = TRACK_STATE_RUNNING;
}
if (new_state == module->state)
return MMAL_SUCCESS; /* Nothing to do */
if (module->state == TRACK_STATE_STOPPED && new_state == TRACK_STATE_RUNNING)
{
module->track->start();
}
else if (module->state == TRACK_STATE_RUNNING)
{
if (new_state == TRACK_STATE_STOPPED)
module->track->stop();
else if (new_state == TRACK_STATE_PAUSED)
module->track->pause();
}
else if (module->state == TRACK_STATE_PAUSED)
{
if (new_state == TRACK_STATE_STOPPED)
module->track->stop();
else if (new_state == TRACK_STATE_RUNNING)
module->track->start();
}
module->state = new_state;
return MMAL_SUCCESS;
}
/** Destroy a previously created component */
static MMAL_STATUS_T aaf_component_destroy(MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
aaf_track_destroy(component);
if(component->input_num)
mmal_ports_free(component->input, component->input_num);
if(component->clock_num)
mmal_ports_clock_free(component->clock, component->clock_num);
if(module->format)
mmal_format_free(module->format);
if(module->queue)
mmal_queue_destroy(module->queue);
delete module->lock;
vcos_free(module);
return MMAL_SUCCESS;
}
/** Set format on a port */
static MMAL_STATUS_T aaf_port_set_format(MMAL_PORT_T *port)
{
MMAL_COMPONENT_T *component = port->component;
if (!mmal_format_compare(port->format, component->priv->module->format))
return MMAL_SUCCESS;
/* Check the format is supported */
if (encoding_to_audio_format(port->format->encoding) == AUDIO_FORMAT_INVALID)
{
LOG_ERROR("port does not support '%4.4s'", (char *)&port->format->encoding);
return MMAL_EINVAL;
}
/* Specific checks for PCM */
if (port->format->encoding == MMAL_ENCODING_PCM_SIGNED)
{
if (port->format->es->audio.bits_per_sample != 16 &&
port->format->es->audio.bits_per_sample != 32)
{
LOG_ERROR("port does not support '%4.4s' at %ibps",
(char *)&port->format->encoding,
port->format->es->audio.bits_per_sample);
return MMAL_EINVAL;
}
if (!audio_channel_out_mask_from_count(port->format->es->audio.channels))
{
LOG_ERROR("%s invalid channels mask from %i", port->name,
(int)port->format->es->audio.channels);
return MMAL_ENOSYS;
}
}
mmal_format_copy(component->priv->module->format, port->format);
return aaf_track_create(component);
}
/** Enable processing on a port */
static MMAL_STATUS_T aaf_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_STATUS_T status = MMAL_SUCCESS;
MMAL_PARAM_UNUSED(cb);
if (module->track == NULL)
status = aaf_port_set_format(port);
if (status != MMAL_SUCCESS)
return status;
module->is_enabled = MMAL_TRUE;
aaf_track_state_update(component);
return MMAL_SUCCESS;
}
/** Flush a port */
static MMAL_STATUS_T aaf_port_flush(MMAL_PORT_T *port)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_BUFFER_HEADER_T *buffer;
while((buffer = mmal_queue_get(module->queue)))
mmal_port_buffer_header_callback(port, buffer);
module->lock->lock();
module->bytes_queued = 0;
module->lock->unlock();
if (module->track == NULL)
return MMAL_SUCCESS;
module->track->stop();
module->track->flush();
module->state = TRACK_STATE_STOPPED;
aaf_track_state_update(component);
return MMAL_SUCCESS;
}
/** Disable processing on a port */
static MMAL_STATUS_T aaf_port_disable(MMAL_PORT_T *port)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
module->is_enabled = MMAL_FALSE;
aaf_track_state_update(component);
return aaf_port_flush(port);
}
static MMAL_STATUS_T aaf_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
unsigned int bits_per_sample, channels, sample_rate;
uint32_t aaf_bytes_queued = 0;
int64_t latency, ts;
/* Handle event buffers */
if (buffer->cmd)
{
LOG_ERROR("discarding event %i on port %p", (int)buffer->cmd, port);
buffer->length = 0;
mmal_port_buffer_header_callback(port, buffer);
return MMAL_SUCCESS;
}
if (module->status != MMAL_SUCCESS)
return module->status;
bits_per_sample = port->format->es->audio.bits_per_sample;
channels = port->format->es->audio.channels;
sample_rate = port->format->es->audio.sample_rate;
if (port->format->encoding == MMAL_ENCODING_AC3 ||
port->format->encoding == MMAL_ENCODING_EAC3)
{
uint32_t aaf_latency = 0;
AudioSystem::getOutputLatency(&aaf_latency, AUDIO_STREAM_MUSIC);
latency = aaf_latency * 1000LL;
bits_per_sample = 16;
channels = 2;
if (port->format->encoding == MMAL_ENCODING_EAC3 &&
sample_rate <= 48000)
sample_rate *= 4;
aaf_bytes_queued = module->track->frameCount();
}
else
{
latency = module->track->latency() * 1000LL;
}
/* Keep aaf_track_callback from sending more samples */
module->lock->lock();
module->bytes_queued += buffer->length;
latency += (module->bytes_queued + aaf_bytes_queued) / channels /
(bits_per_sample / 8) * 1000000LL / sample_rate;
ts = buffer->pts - latency;
module->lock->unlock();
mmal_port_clock_media_time_set(component->clock[0], ts);
mmal_queue_put(module->queue, buffer);
return MMAL_SUCCESS;
}
void aaf_clock_event(MMAL_PORT_T *port, const MMAL_CLOCK_EVENT_T *event)
{
MMAL_COMPONENT_T *component = port->component;
switch (event->id)
{
case MMAL_CLOCK_EVENT_SCALE:
aaf_track_state_update(component);
break;
case MMAL_CLOCK_EVENT_TIME:
case MMAL_CLOCK_EVENT_REFERENCE:
case MMAL_CLOCK_EVENT_ACTIVE:
/* nothing to do */
break;
default:
LOG_DEBUG("unknown event id %4.4s", (char*)&event->id);
break;
}
}
/** Create an instance of a component */
static MMAL_STATUS_T mmal_component_create_aaf(const char *name, MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module;
/* Check we're the requested component */
if(strcmp(name, "aaf." MMAL_AUDIO_RENDER))
return MMAL_ENOENT;
/* Allocate our module context */
component->priv->module = module = (MMAL_COMPONENT_MODULE_T *)vcos_calloc(1, sizeof(*module), "mmal module");
if(!module)
return MMAL_ENOMEM;
module->lock = new Mutex();
if (!module->lock)
goto error;
/* Allocate the ports for this component */
component->input = mmal_ports_alloc(component, 1, MMAL_PORT_TYPE_INPUT, 0);
if(!component->input)
goto error;
component->input_num = 1;
/* Create the clock port (clock ports are managed by the framework) */
component->clock = mmal_ports_clock_alloc(component, 1, 0, aaf_clock_event);
if (!component->clock)
goto error;
component->clock_num = 1;
module->queue = mmal_queue_create();
if(!module->queue)
goto error;
module->format = mmal_format_alloc();
if(!module->format)
goto error;
component->input[0]->priv->pf_set_format = aaf_port_set_format;
component->input[0]->priv->pf_enable = aaf_port_enable;
component->input[0]->priv->pf_disable = aaf_port_disable;
component->input[0]->priv->pf_flush = aaf_port_flush;
component->input[0]->priv->pf_send = aaf_port_send;
component->input[0]->buffer_num_min = INPUT_MIN_BUFFER_NUM;
component->input[0]->buffer_num_recommended = INPUT_RECOMMENDED_BUFFER_NUM;
component->input[0]->format->type = MMAL_ES_TYPE_AUDIO;
component->priv->pf_destroy = aaf_component_destroy;
return MMAL_SUCCESS;
error:
aaf_component_destroy(component);
return MMAL_ENOMEM;
}
MMAL_CONSTRUCTOR(mmal_register_component_aaf_audio);
void mmal_register_component_aaf_audio(void)
{
mmal_component_supplier_register("aaf", mmal_component_create_aaf);
}
@@ -0,0 +1,188 @@
/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "mmal.h"
#include "core/mmal_component_private.h"
#include "core/mmal_port_private.h"
#include "util/mmal_graph.h"
#include "mmal_logging.h"
#define AGGREGATOR_PREFIX "aggregator"
#define AGGREGATOR_PIPELINE_PREFIX "pipeline"
typedef struct MMAL_GRAPH_USERDATA_T {
int dummy;
} MMAL_GRAPH_USERDATA_T;
static MMAL_STATUS_T aggregator_parameter_set(MMAL_GRAPH_T *graph,
MMAL_PORT_T *port, const MMAL_PARAMETER_HEADER_T *param)
{
MMAL_PARAM_UNUSED(graph);
MMAL_PARAM_UNUSED(port);
MMAL_PARAM_UNUSED(param);
LOG_TRACE("graph %p, port %p, param %p", graph, port, param);
return MMAL_ENOSYS;
}
static MMAL_STATUS_T aggregator_parameter_get(MMAL_GRAPH_T *graph,
MMAL_PORT_T *port, MMAL_PARAMETER_HEADER_T *param)
{
MMAL_PARAM_UNUSED(graph);
MMAL_PARAM_UNUSED(port);
MMAL_PARAM_UNUSED(param);
LOG_TRACE("graph %p, port %p, param %p", graph, port, param);
return MMAL_ENOSYS;
}
/** Create an instance of a component */
static MMAL_STATUS_T mmal_component_create_aggregator_pipeline(const char *full_name,
const char *component_names, MMAL_COMPONENT_T *component)
{
MMAL_GRAPH_T *graph = 0;
MMAL_COMPONENT_T *subcomponent[2] = {0};
MMAL_STATUS_T status = MMAL_ENOMEM;
unsigned int length;
char *orig, *names;
length = strlen(component_names);
names = orig = vcos_calloc(1, length + 1, "mmal aggregator");
if (!names)
goto error;
memcpy(names, component_names, length);
/* We'll build the aggregator using a graph */
status = mmal_graph_create(&graph, sizeof(MMAL_GRAPH_USERDATA_T));
if (status != MMAL_SUCCESS)
goto error;
graph->pf_parameter_get = aggregator_parameter_get;
graph->pf_parameter_set = aggregator_parameter_set;
/* Iterate through all the specified components */
while (*names)
{
MMAL_CONNECTION_T *connection;
const char *name;
/* Switch to a new connection */
if (subcomponent[0])
mmal_component_destroy(subcomponent[0]);
subcomponent[0] = subcomponent[1];
subcomponent[1] = 0;
/* Extract the name of the next component */
for (name = names; *names && *names != ':'; names++);
/* Replace the separator */
if (*names)
*(names++) = 0;
/* Skip empty strings */
if (!*name)
continue;
status = mmal_component_create(name, &subcomponent[1]);
if (status != MMAL_SUCCESS)
goto error;
status = mmal_graph_add_component(graph, subcomponent[1]);
if (status != MMAL_SUCCESS)
goto error;
/* Special case for dealing with the first component in the chain */
if (!subcomponent[0])
{
/* Add first input port if any */
if (subcomponent[1]->input_num)
{
status = mmal_graph_add_port(graph, subcomponent[1]->input[0]);
if (status != MMAL_SUCCESS)
goto error;
}
continue;
}
/* Create connection between the 2 ports */
if (subcomponent[0]->output_num < 1 || subcomponent[1]->input_num < 1)
goto error;
status = mmal_connection_create(&connection, subcomponent[0]->output[0],
subcomponent[1]->input[0], 0);
if (status != MMAL_SUCCESS)
goto error;
status = mmal_graph_add_connection(graph, connection);
/* Now the connection is added to the graph we don't care about it anymore */
mmal_connection_destroy(connection);
if (status != MMAL_SUCCESS)
goto error;
}
/* Add last output port if any */
if (subcomponent[1] && subcomponent[1]->output_num && subcomponent[1]->output[0])
{
status = mmal_graph_add_port(graph, subcomponent[1]->output[0]);
if (status != MMAL_SUCCESS)
goto error;
}
/* Build the graph */
component->priv->module = (struct MMAL_COMPONENT_MODULE_T *)graph;
status = mmal_graph_component_constructor(full_name, component);
if (status != MMAL_SUCCESS)
goto error;
end:
if (subcomponent[0])
mmal_component_destroy(subcomponent[0]);
if (subcomponent[1])
mmal_component_destroy(subcomponent[1]);
vcos_free(orig);
return status;
error:
if (graph)
mmal_graph_destroy(graph);
goto end;
}
/** Create an instance of a component */
static MMAL_STATUS_T mmal_component_create_aggregator(const char *name, MMAL_COMPONENT_T *component)
{
const char *stripped = name + sizeof(AGGREGATOR_PREFIX);
/* Select the requested aggregator */
if (!strncmp(stripped, AGGREGATOR_PIPELINE_PREFIX ":", sizeof(AGGREGATOR_PIPELINE_PREFIX)))
return mmal_component_create_aggregator_pipeline(name,
stripped + sizeof(AGGREGATOR_PIPELINE_PREFIX), component);
return MMAL_ENOSYS;
}
MMAL_CONSTRUCTOR(mmal_register_component_aggregator);
void mmal_register_component_aggregator(void)
{
mmal_component_supplier_register(AGGREGATOR_PREFIX, mmal_component_create_aggregator);
}
@@ -0,0 +1,861 @@
/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "mmal.h"
#include "core/mmal_component_private.h"
#include "core/mmal_port_private.h"
#include "mmal_logging.h"
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/ICrypto.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <binder/ProcessState.h>
using namespace android;
/* List of variants supported by this component */
#define AMC_VARIANT_UNKNOWN 0
#define AMC_VARIANT_AUDIO_DECODE 1
#define AMC_VARIANT_AUDIO_DECODE_NAME "audio_decode"
/*****************************************************************************/
struct AmcHandler : public AHandler
{
AmcHandler(MMAL_COMPONENT_T *component) :
mComponent(component), mNotificationRequested(false) {}
void requestNotification(sp<MediaCodec> &codec)
{
if (mActivityNotify == NULL)
mActivityNotify = new AMessage(0, id());
if (!mNotificationRequested)
{
mNotificationRequested = true;
codec->requestActivityNotification(mActivityNotify->dup());
}
}
void reset()
{
mNotificationRequested = false;
}
protected:
virtual void onMessageReceived(const sp<AMessage> &msg)
{
(void)msg;
mNotificationRequested = false;
mmal_component_action_trigger(mComponent);
}
private:
MMAL_COMPONENT_T *mComponent;
sp<AMessage> mActivityNotify;
bool mNotificationRequested;
};
typedef struct MMAL_COMPONENT_MODULE_T
{
MMAL_STATUS_T status; /**< current status of the component */
sp<MediaCodec> codec;
sp<AmcHandler> ahandler;
sp<ALooper> alooper;
Vector<sp<ABuffer> > input_buffers; /**< list of buffers exported by mediacodec */
Vector<sp<ABuffer> > output_buffers; /**< list of buffers exported by mediacodec */
} MMAL_COMPONENT_MODULE_T;
typedef struct MMAL_PORT_MODULE_T
{
MMAL_ES_FORMAT_T *format; /**< format currently configured */
MMAL_QUEUE_T *queue; /**< queue for the buffers sent to the ports */
MMAL_BOOL_T needs_configuring; /**< port is waiting for a format commit */
List<size_t> *dequeued; /* buffers already dequeued from the codec */
const char *mime;
unsigned int actual_channels;
} MMAL_PORT_MODULE_T;
static struct encoding_table_t {
const char *mime;
MMAL_FOURCC_T encoding;
MMAL_ES_TYPE_T type;
MMAL_FOURCC_T encoding_variant;
} encoding_list[] =
{ {"audio/3gpp", MMAL_ENCODING_AMRNB, MMAL_ES_TYPE_AUDIO, 0},
{"audio/amr-wb", MMAL_ENCODING_AMRWB, MMAL_ES_TYPE_AUDIO, 0},
{"audio/mpeg", MMAL_ENCODING_MPGA, MMAL_ES_TYPE_AUDIO, 0},
{"audio/mp4a-latm", MMAL_ENCODING_MP4A, MMAL_ES_TYPE_AUDIO, 0},
{"audio/mp4a-latm", MMAL_ENCODING_MP4A, MMAL_ES_TYPE_AUDIO, MMAL_ENCODING_VARIANT_MP4A_ADTS},
{"audio/vorbis", MMAL_ENCODING_VORBIS, MMAL_ES_TYPE_AUDIO, 0},
{"audio/g711-alaw", MMAL_ENCODING_ALAW, MMAL_ES_TYPE_AUDIO, 0},
{"audio/g711-ulaw", MMAL_ENCODING_MULAW, MMAL_ES_TYPE_AUDIO, 0},
{"audio/ac3", MMAL_ENCODING_AC3, MMAL_ES_TYPE_AUDIO, 0},
{"audio/ec3", MMAL_ENCODING_EAC3, MMAL_ES_TYPE_AUDIO, 0},
{"audio/eac3", MMAL_ENCODING_EAC3, MMAL_ES_TYPE_AUDIO, 0},
{"audio/raw", MMAL_ENCODING_PCM_SIGNED, MMAL_ES_TYPE_AUDIO, 0},
{"", MMAL_ENCODING_PCM_SIGNED, MMAL_ES_TYPE_AUDIO, 0},
{ 0, 0, MMAL_ES_TYPE_UNKNOWN, 0}
};
static const char *encoding_to_mime(MMAL_ES_TYPE_T type, MMAL_FOURCC_T encoding,
MMAL_FOURCC_T encoding_variant)
{
struct encoding_table_t *entry = encoding_list;
for (entry = encoding_list; entry->mime; entry++)
if (entry->encoding == encoding && entry->type == type &&
entry->encoding_variant == encoding_variant)
break;
return entry->mime;
}
static void mime_to_encoding(const char *mime,
MMAL_ES_TYPE_T *type, MMAL_FOURCC_T *encoding, MMAL_FOURCC_T *encoding_variant)
{
struct encoding_table_t *entry = encoding_list;
for (entry = encoding_list; entry->mime; entry++)
if (!strcmp(mime, entry->mime))
break;
*encoding = entry->encoding;
*type = entry->type;
*encoding_variant = entry->encoding_variant;
}
/*****************************************************************************/
/** Actual processing functions */
static MMAL_BOOL_T amc_do_input_processing(MMAL_COMPONENT_T *component,
MMAL_BOOL_T *notification)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_PORT_T *port = component->input[0];
MMAL_PORT_MODULE_T *port_module = port->priv->module;
MMAL_BUFFER_HEADER_T *in;
status_t astatus;
size_t size, index;
uint32_t flags = 0;
in = mmal_queue_get(port_module->queue);
/* Get an input buffer from the codec. We dequeue input buffers even
* if we do not any data to process to make sure that we do not get
* flooded with notifications from the codec that buffers are
* available. */
if (port_module->dequeued->empty() || !in)
{
astatus = module->codec->dequeueInputBuffer(&index, 0ll);
if (astatus == OK)
{
LOG_TRACE("dequeueInputBuffer %i", index);
port_module->dequeued->push_back(index);
}
else if (astatus != -EAGAIN)
{
LOG_TRACE("dequeueInputBuffer failed (%i)", astatus);
}
}
/* Check whether we can process data */
if (!in)
{
return 0;
}
else if (port_module->dequeued->empty())
{
mmal_queue_put_back(port_module->queue, in);
/* We have data we want to process so request to be notified as soon
* as the codec is available to process it */
*notification |= MMAL_TRUE;
return 0;
}
/* We have some processing to do */
index = *port_module->dequeued->begin();
sp<ABuffer> inBuf = module->input_buffers.itemAt(index);
if (inBuf->capacity() < in->length)
LOG_ERROR("MediaCodec input buffer too small (%i/%i)",
(int)inBuf->capacity(), (int)in->length);
size = MMAL_MIN(inBuf->capacity(), in->length);
if (in->length)
memcpy(inBuf->data(), in->data + in->offset, size);
if (in->flags & MMAL_BUFFER_HEADER_FLAG_EOS)
flags |= MediaCodec::BUFFER_FLAG_EOS;
if (in->flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME)
flags |= MediaCodec::BUFFER_FLAG_SYNCFRAME;
if (in->flags & MMAL_BUFFER_HEADER_FLAG_CONFIG)
flags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
LOG_TRACE("queueInputBuffer %i %ibytes, %lldus", index, in->length, in->pts);
astatus = module->codec->queueInputBuffer(index, 0, size, in->pts, flags);
if (astatus != OK)
{
LOG_ERROR("queueInputBuffer failed (%i)", astatus);
mmal_event_error_send(component, MMAL_EIO);
module->status = MMAL_EIO;
}
/* Send buffers back */
in->length = 0;
mmal_port_buffer_header_callback(port, in);
port_module->dequeued->erase(port_module->dequeued->begin());
return 1;
}
static void amc_output_format_changed(MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_PORT_T *port = component->output[0];
MMAL_EVENT_FORMAT_CHANGED_T *event;
MMAL_BUFFER_HEADER_T *buffer;
MMAL_STATUS_T status;
int32_t value;
sp<AMessage> format = new AMessage;
status_t astatus = module->codec->getOutputFormat(&format);
LOG_DEBUG("INFO_FORMAT_CHANGED (%i): %s", astatus,
format->debugString().c_str());
/* Get an event buffer */
status = mmal_port_event_get(port, &buffer, MMAL_EVENT_FORMAT_CHANGED);
if (status != MMAL_SUCCESS)
{
LOG_ERROR("unable to get an event buffer");
return;
}
event = mmal_event_format_changed_get(buffer);
AString amime;
format->findString("mime", &amime);
mime_to_encoding(amime.c_str(),
&event->format->type, &event->format->encoding,
&event->format->encoding_variant);
switch (port->format->type)
{
case MMAL_ES_TYPE_VIDEO:
if (format->findInt32("width", &value))
event->format->es->video.width = value;
if (format->findInt32("height", &value))
event->format->es->video.height = value;
break;
case MMAL_ES_TYPE_AUDIO:
if (format->findInt32("channel-count", &value))
event->format->es->audio.channels = value;
if (format->findInt32("sample-rate", &value))
event->format->es->audio.sample_rate = value;
if (format->findInt32("bitrate", &value))
event->format->bitrate = value;
if (event->format->encoding == MMAL_ENCODING_PCM_SIGNED)
event->format->es->audio.bits_per_sample = 16;
break;
default:
break;
}
/* Work-around for the ril audio_render component which only supports
* power of 2 arrangements */
if (event->format->type == MMAL_ES_TYPE_AUDIO &&
event->format->encoding == MMAL_ENCODING_PCM_SIGNED)
{
port->priv->module->actual_channels = event->format->es->audio.channels;
if (event->format->es->audio.channels == 6)
event->format->es->audio.channels = 8;
}
/* Update current format */
mmal_format_copy(port->priv->module->format, event->format);
/* Pass on the buffer requirements */
event->buffer_num_min = port->buffer_num_min;
event->buffer_size_min = port->buffer_size_min;
event->buffer_size_recommended = port->buffer_size_recommended;
event->buffer_num_recommended = port->buffer_num_recommended;
mmal_port_event_send(port, buffer);
}
static MMAL_BOOL_T amc_do_output_processing(MMAL_COMPONENT_T *component,
MMAL_BOOL_T *notification)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_PORT_T *port_out = component->output[0];
MMAL_BUFFER_HEADER_T *out;
status_t astatus;
size_t size, offset, index;
int64_t pts;
uint32_t flags;
if (port_out->priv->module->needs_configuring)
return 0;
out = mmal_queue_get(port_out->priv->module->queue);
if (!out)
{
/* We do not want notifications in that case. We've already filled
* our output buffers so we should really wait to receive more
* output buffers before resuming the processing */
*notification = MMAL_FALSE;
return 0;
}
out->flags = 0;
astatus = module->codec->dequeueOutputBuffer(&index, &offset, &size, &pts, &flags, 0ll);
if (astatus != OK)
{
MMAL_BOOL_T do_more = 0;
switch (astatus)
{
case INFO_OUTPUT_BUFFERS_CHANGED:
LOG_DEBUG("INFO_OUTPUT_BUFFERS_CHANGED");
astatus = module->codec->getOutputBuffers(&module->output_buffers);
do_more = MMAL_TRUE;
break;
case INFO_FORMAT_CHANGED:
amc_output_format_changed(component);
port_out->priv->module->needs_configuring = 1;
do_more = MMAL_TRUE;
break;
case -EAGAIN:
/* We have data we want to process so request to be notified as soon
* as the codec is available to process it */
*notification |= MMAL_TRUE;
break;
default:
LOG_ERROR("dequeueOutputBuffer failed (%i)", astatus);
}
mmal_queue_put_back(port_out->priv->module->queue, out);
return do_more;
}
LOG_TRACE("dequeueOutputBuffer %i, %ibytes, %lldus, flags %x",
index, size, pts, flags);
sp<ABuffer> outBuf = module->output_buffers.itemAt(index);
out->flags = 0;
out->offset = 0;
out->pts = pts;
out->dts = 0;
if (flags & MediaCodec::BUFFER_FLAG_EOS)
out->flags |= MMAL_BUFFER_HEADER_FLAG_EOS;
if (flags & MediaCodec::BUFFER_FLAG_SYNCFRAME)
out->flags |= MMAL_BUFFER_HEADER_FLAG_KEYFRAME;
if (flags & MediaCodec::BUFFER_FLAG_CODECCONFIG)
out->flags |= MMAL_BUFFER_HEADER_FLAG_CONFIG;
if (out->alloc_size < size)
LOG_ERROR("MediaCodec output buffer too big (%i/%i)",
(int)out->alloc_size, (int)size);
size = MMAL_MIN(out->alloc_size, size);
/* Audio render only accepts power of 2 channel configurations */
if (port_out->format->type == MMAL_ES_TYPE_AUDIO &&
port_out->format->encoding == MMAL_ENCODING_PCM_SIGNED &&
port_out->priv->module->actual_channels !=
port_out->format->es->audio.channels)
{
unsigned int valid = port_out->priv->module->actual_channels * 2;
unsigned int pitch = port_out->format->es->audio.channels * 2;
uint8_t *src = outBuf->data() + offset;
uint8_t *dst = out->data;
unsigned int i;
size = size * port_out->format->es->audio.channels /
port_out->priv->module->actual_channels;
size = MMAL_MIN(out->alloc_size, size);
memset(dst, 0, size);
for (i = size / pitch; i; i--, src += valid, dst += pitch)
memcpy(dst, src, valid);
}
else if (size)
memcpy(out->data, outBuf->data() + offset, size);
out->length = size;
/* Send buffers back */
module->codec->releaseOutputBuffer(index);
mmal_port_buffer_header_callback(port_out, out);
return 1;
}
static MMAL_BOOL_T amc_do_processing(MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_BOOL_T do_more, request_notification = MMAL_FALSE;
if (module->codec == NULL)
return 0;
/* Don't do anything if we've already seen an error */
if (module->status != MMAL_SUCCESS)
return 0;
do_more = amc_do_input_processing(component, &request_notification);
do_more |= amc_do_output_processing(component, &request_notification);
if (request_notification)
module->ahandler->requestNotification(module->codec);
return do_more;
}
/*****************************************************************************/
static void amc_do_processing_loop(MMAL_COMPONENT_T *component)
{
while (amc_do_processing(component));
}
/** Destroy a previously created component */
static MMAL_STATUS_T amc_component_destroy(MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
unsigned int i;
for(i = 0; i < component->input_num; i++)
{
if(component->input[i]->priv->module->queue)
mmal_queue_destroy(component->input[i]->priv->module->queue);
if(component->input[i]->priv->module->format)
mmal_format_free(component->input[i]->priv->module->format);
if(component->input[i]->priv->module->dequeued)
delete component->input[i]->priv->module->dequeued;
}
if(component->input_num)
mmal_ports_free(component->input, component->input_num);
for(i = 0; i < component->output_num; i++)
{
if(component->output[i]->priv->module->queue)
mmal_queue_destroy(component->output[i]->priv->module->queue);
if(component->output[i]->priv->module->format)
mmal_format_free(component->output[i]->priv->module->format);
}
if(component->output_num)
mmal_ports_free(component->output, component->output_num);
if (module->codec != NULL)
{
module->codec->stop();
module->codec->release();
}
module->alooper->unregisterHandler(module->ahandler->id());
module->alooper->stop();
delete module;
return MMAL_SUCCESS;
}
static MMAL_STATUS_T amc_codec_start(MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_PORT_T *in = component->input[0];
sp<AMessage> format = new AMessage;
status_t astatus;
const char *mime;
mime = encoding_to_mime(in->format->type, in->format->encoding,
in->format->encoding_variant);
if (!mime)
{
LOG_ERROR("cannot match codec %4.4s(%4.4s) with a mime type",
(char *)&in->format->encoding, (char *)&in->format->encoding_variant);
return MMAL_EINVAL;
}
/* We need to restart MediaCodec when the codec type changes */
if (module->codec == NULL || mime != in->priv->module->mime)
{
LOG_DEBUG("creating codec for %s", mime);
/* Start by releasing any instance we've previously created */
if (module->codec != NULL)
{
module->codec->stop();
module->codec->release();
}
module->codec = MediaCodec::CreateByType(module->alooper, mime, false);
if (module->codec == NULL)
{
LOG_ERROR("cannot instantiate MediaCodec for mime: %s", mime);
return MMAL_EINVAL;
}
in->priv->module->mime = mime;
module->ahandler->reset();
LOG_TRACE("creation done");
}
/* When reusing an existing instance, we just need to stop it */
else
{
module->codec->stop();
}
/* Configure MediaCodec */
switch (in->format->type)
{
case MMAL_ES_TYPE_VIDEO:
format->setInt32("width", in->format->es->video.width);
format->setInt32("height", in->format->es->video.height);
if (in->format->es->video.frame_rate.num && in->format->es->video.frame_rate.den)
format->setFloat("frame-rate", in->format->es->video.frame_rate.num /
(float)in->format->es->video.frame_rate.den);
break;
case MMAL_ES_TYPE_AUDIO:
format->setInt32("channel-count", in->format->es->audio.channels);
format->setInt32("sample-rate", in->format->es->audio.sample_rate);
format->setInt32("bitrate", in->format->bitrate);
break;
default:
break;
}
format->setString("mime", mime);
/* Handle the codec specific data */
if (in->format->extradata_size)
{
sp<ABuffer> csd = new ABuffer(in->format->extradata,
in->format->extradata_size);
csd->meta()->setInt32("csd", true);
csd->meta()->setInt64("timeUs", 0);
format->setBuffer("csd-0", csd);
}
/* Propagate the buffer size setting of the input port to the
* codec */
format->setInt32("max-input-size", in->buffer_size);
LOG_TRACE("configuring: %s", format->debugString().c_str());
astatus = module->codec->configure(format, NULL, NULL, 0);
if (astatus)
{
LOG_ERROR("configure failed (%i)", astatus);
return MMAL_EINVAL;
}
LOG_TRACE("starting");
astatus = module->codec->start();
if (astatus != OK)
{
LOG_ERROR("failed to start codec (%i)", astatus);
return MMAL_EINVAL;
}
LOG_TRACE("started");
return MMAL_SUCCESS;
}
/** Enable processing on a port */
static MMAL_STATUS_T amc_input_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_STATUS_T status;
status_t astatus;
MMAL_PARAM_UNUSED(cb);
/* Make sure the format as been committed */
status = port->priv->pf_set_format(port);
if (status != MMAL_SUCCESS)
{
LOG_ERROR("cannot commit port format (%i)", status);
return status;
}
status = amc_codec_start(component);
if (status != MMAL_SUCCESS)
return status;
astatus = module->codec->getInputBuffers(&module->input_buffers);
if (astatus != OK)
{
LOG_ERROR("failed to get codec input buffers (%i)", astatus);
return MMAL_EINVAL;
}
if (module->input_buffers.size())
LOG_TRACE("%i input buffers of size %i", module->input_buffers.size(),
module->input_buffers.itemAt(0)->capacity());
astatus = module->codec->getOutputBuffers(&module->output_buffers);
if (astatus != OK)
{
LOG_ERROR("failed to get codec output buffers (%i)", astatus);
return MMAL_EINVAL;
}
if (module->output_buffers.size())
LOG_TRACE("%i output buffers of size %i", module->output_buffers.size(),
module->output_buffers.itemAt(0)->capacity());
module->status = MMAL_SUCCESS;
return MMAL_SUCCESS;
}
static MMAL_STATUS_T amc_output_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
{
MMAL_PARAM_UNUSED(port);
MMAL_PARAM_UNUSED(cb);
return MMAL_SUCCESS;
}
/** Flush a port */
static MMAL_STATUS_T amc_port_flush(MMAL_PORT_T *port)
{
MMAL_PORT_MODULE_T *port_module = port->priv->module;
MMAL_BUFFER_HEADER_T *buffer;
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
status_t astatus;
/* Flush buffers that our component is holding on to */
buffer = mmal_queue_get(port_module->queue);
while(buffer)
{
mmal_port_buffer_header_callback(port, buffer);
buffer = mmal_queue_get(port_module->queue);
}
if (port_module->dequeued)
port_module->dequeued->clear();
/* Flush codec itself */
if (port->type == MMAL_PORT_TYPE_INPUT && module->codec != NULL)
{
astatus = module->codec->flush();
if (astatus != OK)
LOG_ERROR("failed to flush codec (%i)", astatus);
}
return MMAL_SUCCESS;
}
/** Disable processing on a port */
static MMAL_STATUS_T amc_port_disable(MMAL_PORT_T *port)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
status_t astatus;
if (port->type == MMAL_PORT_TYPE_INPUT)
{
astatus = module->codec->stop();
if (astatus != OK)
LOG_ERROR("failed to stop codec (%i)", astatus);
module->codec->release();
module->codec = NULL;
}
/* We just need to flush our internal queue */
return amc_port_flush(port);
}
/** Send a buffer header to a port */
static MMAL_STATUS_T amc_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
mmal_queue_put(port->priv->module->queue, buffer);
mmal_component_action_trigger(port->component);
return MMAL_SUCCESS;
}
/** Set format on input port */
static MMAL_STATUS_T amc_input_port_format_commit(MMAL_PORT_T *in)
{
MMAL_STATUS_T status;
const char *mime;
if (in->is_enabled)
return MMAL_EINVAL;
if (!mmal_format_compare(in->format, in->priv->module->format))
return MMAL_SUCCESS;
mime = encoding_to_mime(in->format->type, in->format->encoding,
in->format->encoding_variant);
if (!mime)
{
LOG_ERROR("cannot match codec %4.4s(%4.4s) with a mime type",
(char *)&in->format->encoding, (char *)&in->format->encoding_variant);
return MMAL_EINVAL;
}
/* Note that the MediaCodec object is only created when the input
* port is enabled to avoid deadlocking when we're running inside an
* Android OMX component (you can't create an OMX odec instance while
* you're already instantiating one) */
status = mmal_format_full_copy(in->priv->module->format, in->format);
if (status != MMAL_SUCCESS)
return status;
/* No need to propagate anything to the output port since
* we'll generate a format change event for it later on */
return status;
}
/** Set format on output port */
static MMAL_STATUS_T amc_output_port_format_commit(MMAL_PORT_T *out)
{
/* The format of the output port needs to match the output of
* MediaCodec */
if (mmal_format_compare(out->format, out->priv->module->format))
return MMAL_EINVAL;
out->priv->module->needs_configuring = 0;
mmal_component_action_trigger(out->component);
return MMAL_SUCCESS;
}
/** Create an instance of a component */
static MMAL_STATUS_T mmal_component_create_android_media_codec(const char *name, MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module;
MMAL_STATUS_T status = MMAL_ENOMEM;
const char *variant_name = name + sizeof("amc");
unsigned int variant = AMC_VARIANT_UNKNOWN;
if (!strcmp(variant_name, AMC_VARIANT_AUDIO_DECODE_NAME))
variant = AMC_VARIANT_AUDIO_DECODE;
if (variant == AMC_VARIANT_UNKNOWN)
{
LOG_ERROR("unsupported variant %s", variant_name);
return MMAL_ENOENT;
}
/* Allocate the context for our module */
component->priv->module = module = new MMAL_COMPONENT_MODULE_T;
if (!module)
return MMAL_ENOMEM;
component->priv->pf_destroy = amc_component_destroy;
module->status = MMAL_SUCCESS;
ProcessState::self()->startThreadPool();
module->ahandler = new AmcHandler(component);
module->alooper = new ALooper;
module->alooper->setName("amc_looper");
module->alooper->registerHandler(module->ahandler);
module->alooper->start(false);
/* Allocate and initialise all the ports for this component */
component->input = mmal_ports_alloc(component, 1, MMAL_PORT_TYPE_INPUT, sizeof(MMAL_PORT_MODULE_T));
if(!component->input)
goto error;
component->input_num = 1;
component->input[0]->priv->pf_enable = amc_input_port_enable;
component->input[0]->priv->pf_disable = amc_port_disable;
component->input[0]->priv->pf_flush = amc_port_flush;
component->input[0]->priv->pf_send = amc_port_send;
component->input[0]->priv->pf_set_format = amc_input_port_format_commit;
component->input[0]->buffer_num_min = 1;
component->input[0]->buffer_num_recommended = 3;
component->input[0]->priv->module->queue = mmal_queue_create();
if(!component->input[0]->priv->module->queue)
goto error;
component->input[0]->priv->module->format = mmal_format_alloc();
if(!component->input[0]->priv->module->format)
goto error;
component->input[0]->priv->module->dequeued = new List<size_t>;
if(!component->input[0]->priv->module->dequeued)
goto error;
component->output = mmal_ports_alloc(component, 1, MMAL_PORT_TYPE_OUTPUT, sizeof(MMAL_PORT_MODULE_T));
if(!component->output)
goto error;
component->output_num = 1;
component->output[0]->priv->pf_enable = amc_output_port_enable;
component->output[0]->priv->pf_disable = amc_port_disable;
component->output[0]->priv->pf_flush = amc_port_flush;
component->output[0]->priv->pf_send = amc_port_send;
component->output[0]->priv->pf_set_format = amc_output_port_format_commit;
component->output[0]->buffer_num_min = 1;
component->output[0]->buffer_num_recommended = 3;
component->output[0]->priv->module->queue = mmal_queue_create();
if(!component->output[0]->priv->module->queue)
goto error;
component->output[0]->priv->module->format = mmal_format_alloc();
if(!component->output[0]->priv->module->format)
goto error;
status = mmal_component_action_register(component, amc_do_processing_loop);
if (status != MMAL_SUCCESS)
goto error;
/* Setup ports according to selected variant */
if (variant == AMC_VARIANT_AUDIO_DECODE)
{
component->input[0]->format->type = MMAL_ES_TYPE_AUDIO;
component->input[0]->format->encoding = MMAL_ENCODING_EAC3;
component->input[0]->format->es->audio.sample_rate = 48000;
component->input[0]->format->es->audio.channels = 2;
component->input[0]->buffer_size_min = 4 * 1024;
component->output[0]->format->type = MMAL_ES_TYPE_AUDIO;
component->output[0]->format->encoding = MMAL_ENCODING_PCM_SIGNED;
component->output[0]->format->es->audio.sample_rate = 48000;
component->output[0]->format->es->audio.channels = 2;
component->output[0]->format->es->audio.bits_per_sample = 16;
component->output[0]->buffer_size_min = 32 * 1024;
}
/* Update our current view of the output format */
mmal_format_copy(component->output[0]->priv->module->format,
component->output[0]->format);
return MMAL_SUCCESS;
error:
amc_component_destroy(component);
return status;
}
MMAL_CONSTRUCTOR(mmal_register_component_android_media_codec);
void mmal_register_component_android_media_codec(void)
{
mmal_component_supplier_register("amc", mmal_component_create_android_media_codec);
}
@@ -0,0 +1,287 @@
/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "mmal.h"
#include "core/mmal_component_private.h"
#include "core/mmal_port_private.h"
#include "mmal_logging.h"
#define ARTIFICIAL_CAMERA_PORTS_NUM 3
/* Buffering requirements */
#define OUTPUT_MIN_BUFFER_NUM 1
#define OUTPUT_RECOMMENDED_BUFFER_NUM 4
#define DEFAULT_WIDTH 320
#define DEFAULT_HEIGHT 240
/*****************************************************************************/
typedef struct MMAL_PORT_MODULE_T
{
MMAL_BUFFER_HEADER_VIDEO_SPECIFIC_T frame;
unsigned int frame_size;
int count;
MMAL_QUEUE_T *queue;
} MMAL_PORT_MODULE_T;
typedef struct MMAL_COMPONENT_MODULE_T
{
MMAL_STATUS_T status;
} MMAL_COMPONENT_MODULE_T;
/*****************************************************************************/
static void artificial_camera_do_processing(MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_BUFFER_HEADER_T *buffer;
unsigned int i;
if (module->status != MMAL_SUCCESS)
return;
/* Loop through all the ports */
for (i = 0; i < component->output_num; i++)
{
MMAL_PORT_T *port = component->output[i];
buffer = mmal_queue_get(port->priv->module->queue);
if (!buffer)
continue;
/* Sanity check the buffer size */
if (buffer->alloc_size < port->priv->module->frame_size)
{
LOG_ERROR("buffer too small (%i/%i)",
buffer->alloc_size, port->priv->module->frame_size);
module->status = MMAL_EINVAL;
mmal_queue_put_back(port->priv->module->queue, buffer);
mmal_event_error_send(component, module->status);
return;
}
module->status = mmal_buffer_header_mem_lock(buffer);
if (module->status != MMAL_SUCCESS)
{
LOG_ERROR("invalid buffer (%p, %p)", buffer, buffer->data);
mmal_queue_put_back(port->priv->module->queue, buffer);
mmal_event_error_send(component, module->status);
return;
}
buffer->offset = 0;
buffer->length = port->priv->module->frame_size;
buffer->type->video = port->priv->module->frame;
memset(buffer->data, 0xff, buffer->length);
if (buffer->type->video.planes > 1)
memset(buffer->data + buffer->type->video.offset[1],
0x7f - port->priv->module->count++,
buffer->length - buffer->type->video.offset[1]);
mmal_buffer_header_mem_unlock(buffer);
mmal_port_buffer_header_callback(port, buffer);
}
vcos_sleep(10); /* Make sure we don't peg all the resources */
}
/** Destroy a previously created component */
static MMAL_STATUS_T artificial_camera_component_destroy(MMAL_COMPONENT_T *component)
{
unsigned int i;
for (i = 0; i < component->output_num; i++)
if (component->output[i]->priv->module->queue)
mmal_queue_destroy(component->output[i]->priv->module->queue);
if(component->output_num)
mmal_ports_free(component->output, component->output_num);
vcos_free(component->priv->module);
return MMAL_SUCCESS;
}
/** Enable processing on a port */
static MMAL_STATUS_T artificial_camera_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
{
MMAL_PARAM_UNUSED(port);
MMAL_PARAM_UNUSED(cb);
return MMAL_SUCCESS;
}
/** Flush a port */
static MMAL_STATUS_T artificial_camera_port_flush(MMAL_PORT_T *port)
{
MMAL_PARAM_UNUSED(port);
return MMAL_SUCCESS;
}
/** Disable processing on a port */
static MMAL_STATUS_T artificial_camera_port_disable(MMAL_PORT_T *port)
{
MMAL_PARAM_UNUSED(port);
return MMAL_SUCCESS;
}
/** Send a buffer header to a port */
static MMAL_STATUS_T artificial_camera_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
/* Just queue the buffer */
mmal_queue_put(port->priv->module->queue, buffer);
mmal_component_action_trigger(port->component);
return MMAL_SUCCESS;
}
/** Set format on a port */
static MMAL_STATUS_T artificial_camera_port_format_commit(MMAL_PORT_T *port)
{
MMAL_PORT_MODULE_T *port_module = port->priv->module;
unsigned int width = port->format->es->video.width;
unsigned int height = port->format->es->video.height;
width = (width + 31) & ~31;
height = (height + 15) & ~15;
/* We only support a few formats */
switch(port->format->encoding)
{
case MMAL_ENCODING_I420:
port_module->frame_size = width * height * 3 / 2;
port_module->frame.planes = 3;
port_module->frame.pitch[0] = width;
port_module->frame.offset[1] = port_module->frame.pitch[0] * height;
port_module->frame.pitch[1] = width / 2;
port_module->frame.offset[2] = port_module->frame.offset[1] + port_module->frame.pitch[1] * height / 2;
port_module->frame.pitch[2] = width / 2;
break;
case MMAL_ENCODING_NV21:
port_module->frame_size = width * height * 3 / 2;
port_module->frame.planes = 2;
port_module->frame.pitch[0] = width;
port_module->frame.offset[1] = port_module->frame.pitch[0] * height;
port_module->frame.pitch[1] = width;
break;
case MMAL_ENCODING_I422:
port_module->frame_size = width * height * 2;
port_module->frame.planes = 3;
port_module->frame.pitch[0] = width;
port_module->frame.offset[1] = port_module->frame.pitch[0] * height;
port_module->frame.pitch[1] = width / 2;
port_module->frame.offset[2] = port_module->frame.offset[1] + port_module->frame.pitch[1] * height;
port_module->frame.pitch[2] = width / 2;
break;
default:
return MMAL_ENOSYS;
}
port->buffer_size_min = port->buffer_size_recommended = port_module->frame_size;
return MMAL_SUCCESS;
}
/** Set parameter on a port */
static MMAL_STATUS_T artificial_port_parameter_set(MMAL_PORT_T *port, const MMAL_PARAMETER_HEADER_T *param)
{
MMAL_PARAM_UNUSED(port);
switch (param->id)
{
default:
return MMAL_ENOSYS;
}
}
/** Get parameter on a port */
static MMAL_STATUS_T artificial_port_parameter_get(MMAL_PORT_T *port, MMAL_PARAMETER_HEADER_T *param)
{
MMAL_PARAM_UNUSED(port);
switch (param->id)
{
default:
return MMAL_ENOSYS;
}
}
/** Create an instance of a component */
static MMAL_STATUS_T mmal_component_create_artificial_camera(const char *name, MMAL_COMPONENT_T *component)
{
MMAL_STATUS_T status = MMAL_ENOMEM;
unsigned int i;
MMAL_PARAM_UNUSED(name);
/* Allocate our module context */
component->priv->module = vcos_calloc(1, sizeof(*component->priv->module), "mmal module");
if (!component->priv->module)
return MMAL_ENOMEM;
component->priv->pf_destroy = artificial_camera_component_destroy;
/* Allocate all the ports for this component */
component->output = mmal_ports_alloc(component, ARTIFICIAL_CAMERA_PORTS_NUM, MMAL_PORT_TYPE_OUTPUT,
sizeof(MMAL_PORT_MODULE_T));
if(!component->output)
goto error;
component->output_num = ARTIFICIAL_CAMERA_PORTS_NUM;
for (i = 0; i < component->output_num; i++)
{
component->output[i]->priv->pf_enable = artificial_camera_port_enable;
component->output[i]->priv->pf_disable = artificial_camera_port_disable;
component->output[i]->priv->pf_flush = artificial_camera_port_flush;
component->output[i]->priv->pf_send = artificial_camera_port_send;
component->output[i]->priv->pf_send = artificial_camera_port_send;
component->output[i]->priv->pf_set_format = artificial_camera_port_format_commit;
component->output[i]->priv->pf_parameter_set = artificial_port_parameter_set;
component->output[i]->priv->pf_parameter_get = artificial_port_parameter_get;
component->output[i]->format->type = MMAL_ES_TYPE_VIDEO;
component->output[i]->format->encoding = MMAL_ENCODING_I420;
component->output[i]->format->es->video.width = DEFAULT_WIDTH;
component->output[i]->format->es->video.height = DEFAULT_HEIGHT;
component->output[i]->buffer_num_min = OUTPUT_MIN_BUFFER_NUM;
component->output[i]->buffer_num_recommended = OUTPUT_RECOMMENDED_BUFFER_NUM;
artificial_camera_port_format_commit(component->output[i]);
component->output[i]->priv->module->queue = mmal_queue_create();
if (!component->output[i]->priv->module->queue)
goto error;
}
status = mmal_component_action_register(component, artificial_camera_do_processing);
if (status != MMAL_SUCCESS)
goto error;
return MMAL_SUCCESS;
error:
artificial_camera_component_destroy(component);
return status;
}
MMAL_CONSTRUCTOR(mmal_register_component_artificial_camera);
void mmal_register_component_artificial_camera(void)
{
mmal_component_supplier_register("artificial_camera", mmal_component_create_artificial_camera);
}
@@ -0,0 +1,607 @@
/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "mmal.h"
#include "core/mmal_component_private.h"
#include "core/mmal_port_private.h"
#include "mmal_logging.h"
#include "libavcodec/avcodec.h"
#include "libavutil/mathematics.h"
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT( 52, 23, 0 )
# include "libavformat/avformat.h"
static AVPacket null_packet = {AV_NOPTS_VALUE, AV_NOPTS_VALUE};
# define av_init_packet(a) *(a) = null_packet
#endif
#if LIBAVCODEC_VERSION_MAJOR < 53
# define avcodec_decode_audio3(a,b,c,d) avcodec_decode_audio2(a,b,c,(d)->data,(d)->size)
#endif
#if LIBAVCODEC_VERSION_MAJOR < 54
#define AVSampleFormat SampleFormat
#define AV_SAMPLE_FMT_NONE SAMPLE_FMT_NONE
#define AV_SAMPLE_FMT_U8 SAMPLE_FMT_U8
#define AV_SAMPLE_FMT_S16 SAMPLE_FMT_S16
#define AV_SAMPLE_FMT_S32 SAMPLE_FMT_S32
#define AV_SAMPLE_FMT_FLT SAMPLE_FMT_FLT
#define AV_SAMPLE_FMT_DBL SAMPLE_FMT_DBL
#endif
/* Buffering requirements */
#define INPUT_MIN_BUFFER_SIZE (4*1024)
#define INPUT_MIN_BUFFER_NUM 1
#define INPUT_RECOMMENDED_BUFFER_SIZE INPUT_MIN_BUFFER_SIZE
#define INPUT_RECOMMENDED_BUFFER_NUM 10
#define OUTPUT_MIN_BUFFER_NUM 1
#define OUTPUT_RECOMMENDED_BUFFER_NUM 4
#define OUTPUT_MIN_BUFFER_SIZE 512
#define OUTPUT_RECOMMENDED_BUFFER_SIZE 4096
static uint32_t encoding_to_codecid(uint32_t encoding);
static uint32_t samplefmt_to_encoding(enum AVSampleFormat);
static unsigned int samplefmt_to_sample_size(enum AVSampleFormat samplefmt);
/****************************/
typedef struct MMAL_COMPONENT_MODULE_T
{
MMAL_STATUS_T status;
MMAL_QUEUE_T *queue_in;
MMAL_QUEUE_T *queue_out;
int64_t pts;
int64_t last_pts;
int64_t samples_since_last_pts;
int output_buffer_size;
uint8_t *output_buffer;
uint8_t *output;
int output_size;
AVCodecContext *codec_context;
AVCodec *codec;
enum AVSampleFormat sample_fmt;
int channels;
int sample_rate;
int bits_per_sample;
MMAL_BOOL_T output_needs_configuring;
} MMAL_COMPONENT_MODULE_T;
/** Destroy a previously created component */
static MMAL_STATUS_T avcodec_component_destroy(MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
if (module->codec_context)
{
if (module->codec_context->extradata)
vcos_free(module->codec_context->extradata);
if (module->codec_context->codec)
avcodec_close(module->codec_context);
av_free(module->codec_context);
}
if (module->output_buffer)
av_free(module->output_buffer);
if (module->queue_in)
mmal_queue_destroy(module->queue_in);
if (module->queue_out)
mmal_queue_destroy(module->queue_out);
vcos_free(module);
if (component->input_num)
mmal_ports_free(component->input, 1);
if (component->output_num)
mmal_ports_free(component->output, 1);
return MMAL_SUCCESS;
}
/** Set format on a port */
static MMAL_STATUS_T avcodec_input_port_set_format(MMAL_PORT_T *port)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
enum CodecID codec_id;
AVCodec *codec;
codec_id = encoding_to_codecid(port->format->encoding);
if (codec_id == CODEC_ID_NONE ||
!(codec = avcodec_find_decoder(codec_id)))
{
LOG_ERROR("ffmpeg codec id %d (for %4.4s) not recognized",
codec_id, (char *)&port->format->encoding);
return MMAL_ENXIO;
}
module->output_buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
if (module->output_buffer)
av_free(module->output_buffer);
module->output_buffer = av_malloc(module->output_buffer_size);
module->codec_context->sample_rate = port->format->es->audio.sample_rate;
module->codec_context->channels = port->format->es->audio.channels;
module->codec_context->block_align = port->format->es->audio.block_align;
module->codec_context->bit_rate = port->format->bitrate;
module->codec_context->bits_per_coded_sample = port->format->es->audio.bits_per_sample;
module->codec_context->extradata_size = port->format->extradata_size;
module->codec_context->extradata =
vcos_calloc(1, port->format->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE,
"avcodec extradata");
if (module->codec_context->extradata)
memcpy(module->codec_context->extradata, port->format->extradata,
port->format->extradata_size);
if (codec->capabilities & CODEC_CAP_TRUNCATED)
module->codec_context->flags |= CODEC_FLAG_TRUNCATED;
if (avcodec_open(module->codec_context, codec) < 0)
{
LOG_ERROR("could not open codec");
return MMAL_EIO;
}
/* Set a default format */
if (module->codec_context->sample_fmt == AV_SAMPLE_FMT_NONE)
module->codec_context->sample_fmt = AV_SAMPLE_FMT_S16;
/* Copy format to output */
mmal_format_copy(component->output[0]->format, port->format);
LOG_DEBUG("avcodec output format %i", module->codec_context->sample_fmt);
component->output[0]->format->encoding = samplefmt_to_encoding(module->codec_context->sample_fmt);
component->output[0]->format->es->audio.bits_per_sample =
samplefmt_to_sample_size(module->codec_context->sample_fmt);
component->output[0]->priv->pf_set_format(component->output[0]);
return MMAL_SUCCESS;
}
/** Set format on a port */
static MMAL_STATUS_T avcodec_output_port_set_format(MMAL_PORT_T *port)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
/* Format can only be set to what is output by the codec */
if (samplefmt_to_encoding(module->codec_context->sample_fmt) != port->format->encoding ||
samplefmt_to_sample_size(module->codec_context->sample_fmt) != port->format->es->audio.bits_per_sample)
return MMAL_EINVAL;
if (!port->format->es->audio.sample_rate || !port->format->es->audio.channels)
return MMAL_EINVAL;
module->sample_fmt = module->codec_context->sample_fmt;
module->sample_rate = port->format->es->audio.sample_rate;
module->channels = port->format->es->audio.channels;
module->bits_per_sample = port->format->es->audio.bits_per_sample;
port->component->priv->module->output_needs_configuring = 0;
mmal_component_action_trigger(port->component);
return MMAL_SUCCESS;
}
/** Enable processing on a port */
static MMAL_STATUS_T avcodec_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
{
MMAL_PARAM_UNUSED(port);
MMAL_PARAM_UNUSED(cb);
return MMAL_SUCCESS;
}
/** Flush a port */
static MMAL_STATUS_T avcodec_port_flush(MMAL_PORT_T *port)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_BUFFER_HEADER_T *buffer;
MMAL_QUEUE_T *queue;
if(port->type == MMAL_PORT_TYPE_OUTPUT)
queue = module->queue_out;
else if(port->type == MMAL_PORT_TYPE_INPUT)
queue = module->queue_in;
else
return MMAL_EINVAL;
/* Flush buffers that our component is holding on to.
* If the reading thread is holding onto a buffer it will send it back ASAP as well
* so no need to care about that. */
while((buffer = mmal_queue_get(queue)))
mmal_port_buffer_header_callback(port, buffer);
return MMAL_SUCCESS;
}
/** Disable processing on a port */
static MMAL_STATUS_T avcodec_port_disable(MMAL_PORT_T *port)
{
MMAL_STATUS_T status;
/* Actions are prevented from running at that point so a flush
* will return all buffers. */
status = avcodec_port_flush(port);
if(status != MMAL_SUCCESS)
return status;
return MMAL_SUCCESS;
}
/*****************************************************************************/
static void avcodec_send_event_format_changed(MMAL_COMPONENT_T *component, MMAL_PORT_T *port)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_BUFFER_HEADER_T *buffer = NULL;
MMAL_EVENT_FORMAT_CHANGED_T *event;
/* Get an event buffer */
module->status = mmal_port_event_get(port, &buffer, MMAL_EVENT_FORMAT_CHANGED);
if (module->status != MMAL_SUCCESS)
{
LOG_ERROR("unable to get an event buffer");
return;
}
event = mmal_event_format_changed_get(buffer);
/* Fill in the new format */
mmal_format_copy(event->format, port->format);
event->format->es->audio.sample_rate = module->codec_context->sample_rate;
event->format->es->audio.channels = module->codec_context->channels;
event->format->encoding = samplefmt_to_encoding(module->codec_context->sample_fmt);
event->format->es->audio.bits_per_sample = samplefmt_to_sample_size(module->codec_context->sample_fmt);
/* Pass on the buffer requirements */
event->buffer_num_min = port->buffer_num_min;
event->buffer_size_min = port->buffer_size_min;
event->buffer_size_recommended = event->buffer_size_min;
event->buffer_num_recommended = port->buffer_num_recommended;
module->output_needs_configuring = 1;
mmal_port_event_send(port, buffer);
}
/*****************************************************************************/
static MMAL_STATUS_T avcodec_send_eos(MMAL_COMPONENT_T *component, MMAL_PORT_T *port)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_BUFFER_HEADER_T *out = mmal_queue_get(module->queue_out);
if (!out)
return MMAL_EAGAIN;
out->length = 0;
out->flags = MMAL_BUFFER_HEADER_FLAG_EOS;
mmal_port_buffer_header_callback(port, out);
return MMAL_SUCCESS;
}
/*****************************************************************************/
static MMAL_STATUS_T avcodec_send_frame(MMAL_COMPONENT_T *component, MMAL_PORT_T *port)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_BUFFER_HEADER_T *out;
int size, samples;
/* Detect format changes */
if (module->codec_context->channels != module->channels ||
module->codec_context->sample_rate != module->sample_rate ||
module->codec_context->sample_fmt != module->sample_fmt)
{
avcodec_send_event_format_changed(component, port);
return MMAL_EAGAIN;
}
out = mmal_queue_get(module->queue_out);
if (!out)
return MMAL_EAGAIN;
size = module->output_size;
if (size > (int)out->alloc_size)
size = out->alloc_size;
samples = size / module->channels * 8 / module->bits_per_sample;
size = samples * module->channels * module->bits_per_sample / 8;
out->length = size;
out->pts = module->pts;
out->flags = 0;
memcpy(out->data, module->output, size);
module->output_size -= size;
module->output += size;
if (module->pts != MMAL_TIME_UNKNOWN)
{
module->last_pts = module->pts;
module->samples_since_last_pts = 0;
}
module->pts = MMAL_TIME_UNKNOWN;
module->samples_since_last_pts += samples;
if (out->pts == MMAL_TIME_UNKNOWN)
out->pts = module->last_pts + module->samples_since_last_pts * 1000000 / module->sample_rate;
out->dts = out->pts;
mmal_port_buffer_header_callback(port, out);
return MMAL_SUCCESS;
}
/*****************************************************************************/
static MMAL_BOOL_T avaudio_do_processing(MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_PORT_T *port_in = component->input[0];
MMAL_PORT_T *port_out = component->output[0];
MMAL_BUFFER_HEADER_T *in;
AVPacket avpkt;
int used = 0;
if (module->output_needs_configuring)
return 0;
if (module->output_size &&
avcodec_send_frame(component, port_out) != MMAL_SUCCESS)
return 0;
if (module->output_size)
return 1;
/* Get input buffer to decode */
in = mmal_queue_get(module->queue_in);
if (!in)
return 0;
/* Discard empty buffers. EOS buffers are not discarded since they will be used
* to flush the codec. */
if (!in->length && !(in->flags & MMAL_BUFFER_HEADER_FLAG_EOS))
goto end;
/* Avcodec expects padded input data */
if (in->length && !in->offset)
{
if(in->length + FF_INPUT_BUFFER_PADDING_SIZE <= in->alloc_size)
memset(in->data + in->length, 0, FF_INPUT_BUFFER_PADDING_SIZE);
else
LOG_WARN("could not pad buffer"); // Try to decode the data anyway
}
/* The actual decoding */
av_init_packet(&avpkt);
avpkt.data = in->length ? in->data + in->offset : 0;
avpkt.size = in->length;
module->output_size = module->output_buffer_size;
module->output = module->output_buffer;
used = avcodec_decode_audio3(module->codec_context, (int16_t*)module->output,
&module->output_size, &avpkt);
/* Check for errors */
if (used < 0 || used > (int)in->length)
{
LOG_ERROR("decoding failed (%i), discarding buffer", used);
used = in->length;
}
module->pts = in->dts;
if (module->pts == MMAL_TIME_UNKNOWN)
module->pts = in->pts;
end:
in->offset += used;
in->length -= used;
if (in->length)
{
mmal_queue_put_back(module->queue_in, in);
return 1;
}
/* We want to keep the EOS buffer until all the frames have been flushed */
if ((in->flags & MMAL_BUFFER_HEADER_FLAG_EOS) &&
module->output_size)
{
mmal_queue_put_back(module->queue_in, in);
return 1;
}
/* Send EOS */
if ((in->flags & MMAL_BUFFER_HEADER_FLAG_EOS) &&
avcodec_send_eos(component, port_out) != MMAL_SUCCESS)
{
mmal_queue_put_back(module->queue_in, in);
return 0;
}
in->offset = 0;
mmal_port_buffer_header_callback(port_in, in);
return 1;
}
/*****************************************************************************/
static void avcodec_do_processing_loop(MMAL_COMPONENT_T *component)
{
while (avaudio_do_processing(component));
}
/** Buffer sending */
static MMAL_STATUS_T avcodec_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
if(port->type == MMAL_PORT_TYPE_OUTPUT) mmal_queue_put(module->queue_out, buffer);
if(port->type == MMAL_PORT_TYPE_INPUT) mmal_queue_put(module->queue_in, buffer);
mmal_component_action_trigger(port->component);
return MMAL_SUCCESS;
}
/** Create an instance of a component */
static MMAL_STATUS_T mmal_component_create_avcodec(const char *name, MMAL_COMPONENT_T *component)
{
MMAL_STATUS_T status = MMAL_ENOMEM;
MMAL_COMPONENT_MODULE_T *module;
/* Check we're the requested component */
if(strcmp(name, "avcodec." MMAL_AUDIO_DECODE))
return MMAL_ENOENT;
/* Allocate our module context */
component->priv->module = module = vcos_calloc(1, sizeof(*module), "mmal module");
if(!module)
return MMAL_ENOENT;
/* Allocate the ports for this component */
component->input = mmal_ports_alloc(component, 1, MMAL_PORT_TYPE_INPUT, 0);
if(!component->input) goto error;
component->input_num = 1;
component->output = mmal_ports_alloc(component, 1, MMAL_PORT_TYPE_OUTPUT, 0);
if(!component->output) goto error;
component->output_num = 1;
module->queue_in = mmal_queue_create();
if(!module->queue_in) goto error;
module->queue_out = mmal_queue_create();
if(!module->queue_out) goto error;
module->codec_context = avcodec_alloc_context();
if(!module->codec_context) goto error;
component->input[0]->priv->pf_set_format = avcodec_input_port_set_format;
component->input[0]->priv->pf_enable = avcodec_port_enable;
component->input[0]->priv->pf_disable = avcodec_port_disable;
component->input[0]->priv->pf_flush = avcodec_port_flush;
component->input[0]->priv->pf_send = avcodec_port_send;
component->input[0]->buffer_num_min = INPUT_MIN_BUFFER_NUM;
component->input[0]->buffer_num_recommended = INPUT_RECOMMENDED_BUFFER_NUM;
component->input[0]->buffer_size_min = INPUT_MIN_BUFFER_SIZE;
component->input[0]->buffer_size_recommended = INPUT_RECOMMENDED_BUFFER_SIZE;
component->output[0]->priv->pf_set_format = avcodec_output_port_set_format;
component->output[0]->priv->pf_enable = avcodec_port_enable;
component->output[0]->priv->pf_disable = avcodec_port_disable;
component->output[0]->priv->pf_disable = avcodec_port_flush;
component->output[0]->priv->pf_send = avcodec_port_send;
component->output[0]->buffer_num_min = OUTPUT_MIN_BUFFER_NUM;
component->output[0]->buffer_num_recommended = OUTPUT_RECOMMENDED_BUFFER_NUM;
component->output[0]->buffer_size_min = OUTPUT_MIN_BUFFER_SIZE;
component->output[0]->buffer_size_recommended = OUTPUT_RECOMMENDED_BUFFER_SIZE;
component->output[0]->format->type = MMAL_ES_TYPE_AUDIO;
component->output[0]->format->encoding = MMAL_ENCODING_PCM_SIGNED_LE;
component->priv->pf_destroy = avcodec_component_destroy;
status = mmal_component_action_register(component, avcodec_do_processing_loop);
if (status != MMAL_SUCCESS)
goto error;
return MMAL_SUCCESS;
error:
avcodec_component_destroy(component);
return status;
}
static struct {
uint32_t encoding;
int codecid;
} codec_to_encoding_table[] =
{
{MMAL_ENCODING_MP4A, CODEC_ID_AAC},
{MMAL_ENCODING_MPGA, CODEC_ID_MP3},
{MMAL_ENCODING_ALAW, CODEC_ID_PCM_ALAW},
{MMAL_ENCODING_MULAW, CODEC_ID_PCM_MULAW},
{MMAL_ENCODING_ADPCM_MS, CODEC_ID_ADPCM_MS},
{MMAL_ENCODING_ADPCM_IMA_MS, CODEC_ID_ADPCM_IMA_WAV},
{MMAL_ENCODING_ADPCM_SWF, CODEC_ID_ADPCM_SWF},
{MMAL_ENCODING_WMA1, CODEC_ID_WMAV1},
{MMAL_ENCODING_WMA2, CODEC_ID_WMAV2},
{MMAL_ENCODING_WMAP, CODEC_ID_WMAPRO},
{MMAL_ENCODING_WMAL, CODEC_ID_NONE},
{MMAL_ENCODING_WMAV, CODEC_ID_WMAVOICE},
{MMAL_ENCODING_AMRNB, CODEC_ID_AMR_NB},
{MMAL_ENCODING_AMRWB, CODEC_ID_AMR_WB},
{MMAL_ENCODING_AMRWBP, CODEC_ID_NONE},
{MMAL_ENCODING_AC3, CODEC_ID_AC3},
{MMAL_ENCODING_EAC3, CODEC_ID_EAC3},
{MMAL_ENCODING_DTS, CODEC_ID_DTS},
{MMAL_ENCODING_MLP, CODEC_ID_MLP},
{MMAL_ENCODING_FLAC, CODEC_ID_FLAC},
{MMAL_ENCODING_VORBIS, CODEC_ID_VORBIS},
{MMAL_ENCODING_SPEEX, CODEC_ID_SPEEX},
{MMAL_ENCODING_NELLYMOSER, CODEC_ID_NELLYMOSER},
{MMAL_ENCODING_QCELP, CODEC_ID_QCELP},
{MMAL_ENCODING_UNKNOWN, CODEC_ID_NONE}
};
static uint32_t encoding_to_codecid(uint32_t encoding)
{
unsigned int i;
for(i = 0; codec_to_encoding_table[i].encoding != MMAL_ENCODING_UNKNOWN; i++)
if(codec_to_encoding_table[i].encoding == encoding) break;
return codec_to_encoding_table[i].codecid;
}
static struct {
uint32_t encoding;
enum AVSampleFormat samplefmt;
unsigned int sample_size;
} samplefmt_to_encoding_table[] =
{
{MMAL_ENCODING_PCM_UNSIGNED, AV_SAMPLE_FMT_U8, 8},
{MMAL_ENCODING_PCM_SIGNED, AV_SAMPLE_FMT_S16, 16},
{MMAL_ENCODING_PCM_SIGNED, AV_SAMPLE_FMT_S32, 32},
{MMAL_ENCODING_PCM_FLOAT, AV_SAMPLE_FMT_FLT, 32},
{MMAL_ENCODING_PCM_FLOAT, AV_SAMPLE_FMT_DBL, 64},
{MMAL_ENCODING_UNKNOWN, AV_SAMPLE_FMT_NONE, 1}
};
static uint32_t samplefmt_to_encoding(enum AVSampleFormat samplefmt)
{
unsigned int i;
for(i = 0; samplefmt_to_encoding_table[i].encoding != MMAL_ENCODING_UNKNOWN; i++)
if(samplefmt_to_encoding_table[i].samplefmt == samplefmt) break;
return samplefmt_to_encoding_table[i].encoding;
}
static unsigned int samplefmt_to_sample_size(enum AVSampleFormat samplefmt)
{
unsigned int i;
for(i = 0; samplefmt_to_encoding_table[i].encoding != MMAL_ENCODING_UNKNOWN; i++)
if(samplefmt_to_encoding_table[i].samplefmt == samplefmt) break;
return samplefmt_to_encoding_table[i].sample_size;
}
MMAL_CONSTRUCTOR(mmal_register_component_avcodec_audio);
void mmal_register_component_avcodec_audio(void)
{
avcodec_init();
avcodec_register_all();
mmal_component_supplier_register("avcodec", mmal_component_create_avcodec);
}
@@ -0,0 +1,586 @@
/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "mmal.h"
#include "core/mmal_component_private.h"
#include "core/mmal_port_private.h"
#include "mmal_logging.h"
#define attribute_deprecated
#include "libavcodec/avcodec.h"
#include "libavutil/mathematics.h"
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT( 52, 23, 0 )
# include "libavformat/avformat.h"
static AVPacket null_packet = {AV_NOPTS_VALUE, AV_NOPTS_VALUE};
# define av_init_packet(a) *(a) = null_packet
#endif
#if LIBAVCODEC_VERSION_MAJOR < 53
# define avcodec_decode_video2(a,b,c,d) avcodec_decode_video(a,b,c,(d)->data,(d)->size)
#endif
/* Buffering requirements */
#define INPUT_MIN_BUFFER_SIZE (800*1024)
#define INPUT_MIN_BUFFER_NUM 1
#define INPUT_RECOMMENDED_BUFFER_SIZE INPUT_MIN_BUFFER_SIZE
#define INPUT_RECOMMENDED_BUFFER_NUM 10
#define OUTPUT_MIN_BUFFER_NUM 1
#define OUTPUT_RECOMMENDED_BUFFER_NUM 4
#define DEFAULT_WIDTH 320
#define DEFAULT_HEIGHT 240
static uint32_t encoding_to_codecid(uint32_t encoding);
static uint32_t pixfmt_to_encoding(enum PixelFormat);
/****************************/
typedef struct MMAL_COMPONENT_MODULE_T
{
MMAL_STATUS_T status;
MMAL_QUEUE_T *queue_in;
MMAL_QUEUE_T *queue_out;
int picture_available;
int64_t pts;
int64_t dts;
AVFrame *picture;
AVCodecContext *codec_context;
AVCodec *codec;
int width;
int height;
enum PixelFormat pix_fmt;
AVPicture layout;
unsigned int planes;
int frame_size;
MMAL_BOOL_T output_needs_configuring;
} MMAL_COMPONENT_MODULE_T;
/** Destroy a previously created component */
static MMAL_STATUS_T avcodec_component_destroy(MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
if (module->picture)
av_free(module->picture);
if (module->codec_context)
{
if(module->codec_context->extradata) vcos_free(module->codec_context->extradata);
if(module->codec_context->codec) avcodec_close(module->codec_context);
av_free(module->codec_context);
}
if(module->queue_in) mmal_queue_destroy(module->queue_in);
if(module->queue_out) mmal_queue_destroy(module->queue_out);
vcos_free(module);
if(component->input_num) mmal_ports_free(component->input, 1);
if(component->output_num) mmal_ports_free(component->output, 1);
return MMAL_SUCCESS;
}
/** Set format on a port */
static MMAL_STATUS_T avcodec_input_port_set_format(MMAL_PORT_T *port)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
enum CodecID codec_id;
AVCodec *codec;
codec_id = encoding_to_codecid(port->format->encoding);
if(codec_id == CODEC_ID_NONE ||
!(codec = avcodec_find_decoder(codec_id)))
{
LOG_ERROR("ffmpeg codec id %d (for %4.4s) not recognized",
codec_id, (char *)&port->format->encoding);
return MMAL_ENXIO;
}
module->picture = avcodec_alloc_frame();
module->codec_context->width = port->format->es->video.width;
module->codec_context->height = port->format->es->video.height;
module->codec_context->extradata_size = port->format->extradata_size;
module->codec_context->extradata =
vcos_calloc(1, port->format->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE,
"avcodec extradata");
if(module->codec_context->extradata)
memcpy(module->codec_context->extradata, port->format->extradata,
port->format->extradata_size);
if (codec->capabilities & CODEC_CAP_TRUNCATED)
module->codec_context->flags |= CODEC_FLAG_TRUNCATED;
if (avcodec_open(module->codec_context, codec) < 0)
{
LOG_ERROR("could not open codec");
return MMAL_EIO;
}
/* Set a default format */
if (module->codec_context->pix_fmt == PIX_FMT_NONE)
module->codec_context->pix_fmt = PIX_FMT_YUV420P;
/* Copy format to output */
LOG_DEBUG("avcodec output format %i (%ix%i)", module->codec_context->pix_fmt,
module->codec_context->width, module->codec_context->height);
port->format->es->video.width = module->codec_context->width;
port->format->es->video.height = module->codec_context->height;
mmal_format_copy(component->output[0]->format, port->format);
component->output[0]->format->encoding = pixfmt_to_encoding(module->codec_context->pix_fmt);
if (!component->output[0]->format->es->video.width)
component->output[0]->format->es->video.width = DEFAULT_WIDTH;
if (!component->output[0]->format->es->video.height)
component->output[0]->format->es->video.height = DEFAULT_HEIGHT;
component->output[0]->priv->pf_set_format(component->output[0]);
return MMAL_SUCCESS;
}
/** Set format on a port */
static MMAL_STATUS_T avcodec_output_port_set_format(MMAL_PORT_T *port)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
/* Format can only be set to what is output by the codec */
if (pixfmt_to_encoding(module->codec_context->pix_fmt) != port->format->encoding)
return MMAL_EINVAL;
module->pix_fmt = module->codec_context->pix_fmt;
module->width = port->format->es->video.width;
module->height = port->format->es->video.height;
module->frame_size =
avpicture_fill(&module->layout, 0, module->pix_fmt, module->width, module->height);
if (module->frame_size < 0)
return MMAL_EINVAL;
/* Calculate the number of planes for this format */
for (module->planes = 0; module->planes < 4; )
if (!module->layout.data[module->planes++])
break;
port->buffer_size_min = module->frame_size;
port->component->priv->module->output_needs_configuring = 0;
mmal_component_action_trigger(port->component);
return MMAL_SUCCESS;
}
/** Enable processing on a port */
static MMAL_STATUS_T avcodec_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
{
MMAL_PARAM_UNUSED(port);
MMAL_PARAM_UNUSED(cb);
return MMAL_SUCCESS;
}
/** Flush a port */
static MMAL_STATUS_T avcodec_port_flush(MMAL_PORT_T *port)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_BUFFER_HEADER_T *buffer;
MMAL_QUEUE_T *queue;
if(port->type == MMAL_PORT_TYPE_OUTPUT)
queue = module->queue_out;
else if(port->type == MMAL_PORT_TYPE_INPUT)
queue = module->queue_in;
else
return MMAL_EINVAL;
/* Flush buffers that our component is holding on to.
* If the reading thread is holding onto a buffer it will send it back ASAP as well
* so no need to care about that. */
while((buffer = mmal_queue_get(queue)))
mmal_port_buffer_header_callback(port, buffer);
return MMAL_SUCCESS;
}
/** Disable processing on a port */
static MMAL_STATUS_T avcodec_port_disable(MMAL_PORT_T *port)
{
MMAL_STATUS_T status;
/* Actions are prevented from running at that point so a flush
* will return all buffers. */
status = avcodec_port_flush(port);
if(status != MMAL_SUCCESS)
return status;
return MMAL_SUCCESS;
}
/*****************************************************************************/
static void avcodec_send_event_format_changed(MMAL_COMPONENT_T *component, MMAL_PORT_T *port)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_BUFFER_HEADER_T *buffer = NULL;
MMAL_EVENT_FORMAT_CHANGED_T *event;
/* Get an event buffer */
module->status = mmal_port_event_get(port, &buffer, MMAL_EVENT_FORMAT_CHANGED);
if (module->status != MMAL_SUCCESS)
{
LOG_ERROR("unable to get an event buffer");
return;
}
event = mmal_event_format_changed_get(buffer);
/* Fill in the new format */
mmal_format_copy(event->format, port->format);
event->format->es->video.width = module->codec_context->width;
event->format->es->video.height = module->codec_context->height;
event->format->encoding = pixfmt_to_encoding(module->codec_context->pix_fmt);
/* Pass on the buffer requirements */
event->buffer_num_min = port->buffer_num_min;
event->buffer_size_min = module->codec_context->width * module->codec_context->height * 2;
event->buffer_size_recommended = event->buffer_size_min;
event->buffer_num_recommended = port->buffer_num_recommended;
module->output_needs_configuring = 1;
mmal_port_event_send(port, buffer);
}
/*****************************************************************************/
static MMAL_STATUS_T avcodec_send_eos(MMAL_COMPONENT_T *component, MMAL_PORT_T *port)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_BUFFER_HEADER_T *out = mmal_queue_get(module->queue_out);
if (!out)
return MMAL_EAGAIN;
out->length = 0;
out->flags = MMAL_BUFFER_HEADER_FLAG_EOS;
mmal_port_buffer_header_callback(port, out);
return MMAL_SUCCESS;
}
/*****************************************************************************/
static MMAL_STATUS_T avcodec_send_picture(MMAL_COMPONENT_T *component, MMAL_PORT_T *port)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_BUFFER_HEADER_T *out;
int i, size;
/* Detect format changes */
if (module->codec_context->width != module->width ||
module->codec_context->height != module->height ||
module->codec_context->pix_fmt != module->pix_fmt)
{
avcodec_send_event_format_changed(component, port);
return MMAL_EAGAIN;
}
out = mmal_queue_get(module->queue_out);
if (!out)
return MMAL_EAGAIN;
size = avpicture_layout((AVPicture *)module->picture, module->pix_fmt,
module->width, module->height, out->data, out->alloc_size);
if (size < 0)
{
mmal_queue_put_back(module->queue_out, out);
LOG_ERROR("avpicture_layout failed: %i, %i, %i, %i",module->pix_fmt,
module->width, module->height, out->alloc_size );
mmal_event_error_send(component, MMAL_EINVAL);
return MMAL_EINVAL;
}
out->length = size;
out->pts = module->pts;
out->flags = 0;
out->type->video.planes = module->planes;
for (i = 0; i < 3; i++)
{
out->type->video.offset[i] = (uint64_t)module->layout.data[i];
out->type->video.pitch[i] = module->layout.linesize[i];
}
mmal_port_buffer_header_callback(port, out);
return MMAL_SUCCESS;
}
/*****************************************************************************/
static MMAL_BOOL_T avcodec_do_processing(MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_PORT_T *port_in = component->input[0];
MMAL_PORT_T *port_out = component->output[0];
MMAL_BUFFER_HEADER_T *in;
AVPacket avpkt;
int used = 0;
if (module->output_needs_configuring)
return 0;
if (module->picture_available &&
avcodec_send_picture(component, port_out) != MMAL_SUCCESS)
return 0;
module->picture_available = 0;
/* Get input buffer to decode */
in = mmal_queue_get(module->queue_in);
if (!in)
return 0;
/* Discard empty buffers. EOS buffers are not discarded since they will be used
* to flush the codec. */
if (!in->length && !(in->flags & MMAL_BUFFER_HEADER_FLAG_EOS))
goto end;
/* Avcodec expects padded input data */
if (in->length && !in->offset)
{
if(in->length + FF_INPUT_BUFFER_PADDING_SIZE <= in->alloc_size)
memset(in->data + in->length, 0, FF_INPUT_BUFFER_PADDING_SIZE);
else
LOG_WARN("could not pad buffer"); // Try to decode the data anyway
}
/* The actual decoding */
module->codec_context->reordered_opaque = in->pts;
av_init_packet(&avpkt);
avpkt.data = in->length ? in->data + in->offset : 0;
avpkt.size = in->length;
used = avcodec_decode_video2(module->codec_context, module->picture,
&module->picture_available, &avpkt);
/* Check for errors */
if (used < 0 || used > (int)in->length)
{
LOG_ERROR("decoding failed (%i), discarding buffer", used);
used = in->length;
}
if (module->picture_available)
{
module->pts = module->picture->reordered_opaque;
if (module->pts == MMAL_TIME_UNKNOWN)
module->pts = in->dts;
}
end:
in->offset += used;
in->length -= used;
if (in->length)
{
mmal_queue_put_back(module->queue_in, in);
return 1;
}
/* We want to keep the EOS buffer until all the frames have been flushed */
if ((in->flags & MMAL_BUFFER_HEADER_FLAG_EOS) &&
module->picture_available)
{
mmal_queue_put_back(module->queue_in, in);
return 1;
}
/* Send EOS */
if ((in->flags & MMAL_BUFFER_HEADER_FLAG_EOS) &&
avcodec_send_eos(component, port_out) != MMAL_SUCCESS)
{
mmal_queue_put_back(module->queue_in, in);
return 0;
}
in->offset = 0;
mmal_port_buffer_header_callback(port_in, in);
return 1;
}
/*****************************************************************************/
static void avcodec_do_processing_loop(MMAL_COMPONENT_T *component)
{
while (avcodec_do_processing(component));
}
/** Buffer sending */
static MMAL_STATUS_T avcodec_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
if(port->type == MMAL_PORT_TYPE_OUTPUT) mmal_queue_put(module->queue_out, buffer);
if(port->type == MMAL_PORT_TYPE_INPUT) mmal_queue_put(module->queue_in, buffer);
mmal_component_action_trigger(port->component);
return MMAL_SUCCESS;
}
/** Create an instance of a component */
static MMAL_STATUS_T mmal_component_create_avcodec(const char *name, MMAL_COMPONENT_T *component)
{
MMAL_STATUS_T status = MMAL_ENOMEM;
MMAL_COMPONENT_MODULE_T *module;
/* Check we're the requested component */
if(strcmp(name, "avcodec." MMAL_VIDEO_DECODE))
return MMAL_ENOENT;
/* Allocate our module context */
component->priv->module = module = vcos_malloc(sizeof(*module), "mmal module");
memset(module, 0, sizeof(*module));
/* Allocate the ports for this component */
component->input = mmal_ports_alloc(component, 1, MMAL_PORT_TYPE_INPUT, 0);
if(!component->input) goto error;
component->input_num = 1;
component->output = mmal_ports_alloc(component, 1, MMAL_PORT_TYPE_OUTPUT, 0);
if(!component->output) goto error;
component->output_num = 1;
module->queue_in = mmal_queue_create();
if(!module->queue_in) goto error;
module->queue_out = mmal_queue_create();
if(!module->queue_out) goto error;
module->codec_context = avcodec_alloc_context();
if(!module->codec_context) goto error;
component->input[0]->priv->pf_set_format = avcodec_input_port_set_format;
component->input[0]->priv->pf_enable = avcodec_port_enable;
component->input[0]->priv->pf_disable = avcodec_port_disable;
component->input[0]->priv->pf_flush = avcodec_port_flush;
component->input[0]->priv->pf_send = avcodec_port_send;
component->input[0]->buffer_num_min = INPUT_MIN_BUFFER_NUM;
component->input[0]->buffer_num_recommended = INPUT_RECOMMENDED_BUFFER_NUM;
component->input[0]->buffer_size_min = INPUT_MIN_BUFFER_SIZE;
component->input[0]->buffer_size_recommended = INPUT_RECOMMENDED_BUFFER_SIZE;
component->output[0]->priv->pf_set_format = avcodec_output_port_set_format;
component->output[0]->priv->pf_enable = avcodec_port_enable;
component->output[0]->priv->pf_disable = avcodec_port_disable;
component->output[0]->priv->pf_flush = avcodec_port_flush;
component->output[0]->priv->pf_send = avcodec_port_send;
component->output[0]->buffer_num_min = OUTPUT_MIN_BUFFER_NUM;
component->output[0]->buffer_num_recommended = OUTPUT_RECOMMENDED_BUFFER_NUM;
component->output[0]->format->type = MMAL_ES_TYPE_VIDEO;
component->output[0]->format->encoding = MMAL_ENCODING_I420;
component->output[0]->format->es->video.width = DEFAULT_WIDTH;
component->output[0]->format->es->video.height = DEFAULT_HEIGHT;
component->priv->pf_destroy = avcodec_component_destroy;
status = mmal_component_action_register(component, avcodec_do_processing_loop);
if (status != MMAL_SUCCESS)
goto error;
return MMAL_SUCCESS;
error:
avcodec_component_destroy(component);
return status;
}
static struct {
uint32_t encoding;
int codecid;
} codec_to_encoding_table[] =
{
{MMAL_ENCODING_H263, CODEC_ID_H263},
{MMAL_ENCODING_H264, CODEC_ID_H264},
{MMAL_ENCODING_MP4V, CODEC_ID_MPEG4},
{MMAL_ENCODING_MP2V, CODEC_ID_MPEG2VIDEO},
{MMAL_ENCODING_MP1V, CODEC_ID_MPEG1VIDEO},
{MMAL_ENCODING_WMV3, CODEC_ID_WMV3},
{MMAL_ENCODING_WMV2, CODEC_ID_WMV2},
{MMAL_ENCODING_WMV1, CODEC_ID_WMV1},
{MMAL_ENCODING_WVC1, CODEC_ID_VC1},
{MMAL_ENCODING_VP6, CODEC_ID_VP6},
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( 52, 68, 2 )
{MMAL_ENCODING_VP8, CODEC_ID_VP8},
#endif
{MMAL_ENCODING_THEORA, CODEC_ID_THEORA},
{MMAL_ENCODING_GIF, CODEC_ID_GIF},
{MMAL_ENCODING_PNG, CODEC_ID_PNG},
{MMAL_ENCODING_PPM, CODEC_ID_PPM},
{MMAL_ENCODING_BMP, CODEC_ID_BMP},
{MMAL_ENCODING_JPEG, CODEC_ID_MJPEG},
{MMAL_ENCODING_UNKNOWN, CODEC_ID_NONE}
};
static uint32_t encoding_to_codecid(uint32_t encoding)
{
unsigned int i;
for(i = 0; codec_to_encoding_table[i].encoding != MMAL_ENCODING_UNKNOWN; i++)
if(codec_to_encoding_table[i].encoding == encoding) break;
return codec_to_encoding_table[i].codecid;
}
static struct {
uint32_t encoding;
enum PixelFormat pixfmt;
} pixfmt_to_encoding_table[] =
{
{MMAL_ENCODING_I420, PIX_FMT_YUV420P},
{MMAL_ENCODING_I422, PIX_FMT_YUV422P},
{MMAL_ENCODING_I420, PIX_FMT_YUVJ420P}, // FIXME
{MMAL_ENCODING_I422, PIX_FMT_YUVJ422P}, // FIXME
{MMAL_ENCODING_RGB16, PIX_FMT_RGB565},
{MMAL_ENCODING_BGR16, PIX_FMT_BGR565},
{MMAL_ENCODING_RGB24, PIX_FMT_RGB24},
{MMAL_ENCODING_BGR24, PIX_FMT_BGR24},
{MMAL_ENCODING_ARGB, PIX_FMT_ARGB},
{MMAL_ENCODING_RGBA, PIX_FMT_RGBA},
{MMAL_ENCODING_ABGR, PIX_FMT_ABGR},
{MMAL_ENCODING_BGRA, PIX_FMT_BGRA},
{MMAL_ENCODING_UNKNOWN, PIX_FMT_NONE}
};
static uint32_t pixfmt_to_encoding(enum PixelFormat pixfmt)
{
unsigned int i;
for(i = 0; pixfmt_to_encoding_table[i].encoding != MMAL_ENCODING_UNKNOWN; i++)
if(pixfmt_to_encoding_table[i].pixfmt == pixfmt) break;
return pixfmt_to_encoding_table[i].encoding;
}
MMAL_CONSTRUCTOR(mmal_register_component_avcodec);
void mmal_register_component_avcodec(void)
{
avcodec_init();
avcodec_register_all();
mmal_component_supplier_register("avcodec", mmal_component_create_avcodec);
}
@@ -0,0 +1,900 @@
/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "mmal.h"
#include "core/mmal_component_private.h"
#include "core/mmal_port_private.h"
#include "util/mmal_util_rational.h"
#include "util/mmal_list.h"
#include "mmal_logging.h"
#define CLOCK_PORTS_NUM 5
#define MAX_CLOCK_EVENT_SLOTS 16
#define DEFAULT_FRAME_RATE 30 /* frames per second */
#define DEFAULT_CLOCK_LATENCY 60000 /* microseconds */
#define FILTER_DURATION 2 /* seconds */
#define MAX_FILTER_LENGTH 180 /* samples */
#define MAX_TIME (~(1LL << 63)) /* microseconds */
#define MIN_TIME (1LL << 63) /* microseconds */
#define ABS(a) ((a) < 0 ? -(a) : (a))
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) > (b) ? (a) : (b))
/** Set to 1 to enable additional stream timing log
* messages used for debugging the clock algorithm */
#define ENABLE_ADDITIONAL_LOGGING 0
static int clock_additional_logging = ENABLE_ADDITIONAL_LOGGING;
/*****************************************************************************/
typedef int64_t TIME_T;
typedef struct FILTER_T
{
uint32_t first; /**< index to the oldest sample */
uint32_t last; /**< index to the most recent sample*/
uint32_t count; /**< total number of samples in the filter */
uint32_t length; /**< maximum number of samples */
TIME_T sum; /**< sum of all samples currently in the filter */
TIME_T h[MAX_FILTER_LENGTH]; /**< filter history */
} FILTER_T;
/** Frame statistics for a stream */
typedef struct CLOCK_STREAM_T
{
uint32_t id; /**< for debug purposes */
MMAL_BOOL_T started; /**< TRUE at least one frame has been received */
TIME_T pts; /**< most recent time-stamp seen */
TIME_T stc; /**< most recent wall-time seen */
TIME_T mt_off; /**< offset of the current time stamp from the
arrival time, i.e. PTS - STC */
TIME_T mt_off_avg; /**< rolling average of the media time offset */
TIME_T mt_off_std; /**< approximate standard deviation of the media
time offset */
FILTER_T avg_filter; /**< moving average filter */
FILTER_T std_filter; /**< (approximate) standard deviation filter */
} CLOCK_STREAM_T;
/** Clock stream events */
typedef enum CLOCK_STREAM_EVENT_T
{
CLOCK_STREAM_EVENT_NONE,
CLOCK_STREAM_EVENT_STARTED, /**< first data received */
CLOCK_STREAM_EVENT_DISCONT, /**< discontinuity detected */
CLOCK_STREAM_EVENT_FRAME_COMPLETE, /**< complete frame received */
} CLOCK_STREAM_EVENT_T;
/** Clock port event */
typedef struct CLOCK_PORT_EVENT_T
{
MMAL_LIST_ELEMENT_T link; /**< must be first */
MMAL_PORT_T *port; /**< clock port where the event occurred */
MMAL_CLOCK_EVENT_T event; /**< event data */
} CLOCK_PORT_EVENT_T;
/** Clock component context */
typedef struct MMAL_COMPONENT_MODULE_T
{
MMAL_STATUS_T status; /**< current status of the component */
MMAL_BOOL_T clock_discont; /**< TRUE -> clock discontinuity detected */
uint32_t stream_min_id; /**< id of selected minimum stream (debugging only) */
uint32_t stream_max_id; /**< if of selected maximum stream (debugging only) */
TIME_T mt_off_target; /**< target clock media time offset */
TIME_T mt_off_clk; /**< current clock media time offset */
TIME_T adj_p; /**< proportional clock adjustment */
TIME_T adj_m; /**< clock adjustment factor (between 1 and 0) */
TIME_T adj; /**< final clock adjustment */
TIME_T stc_at_update; /**< the value of the STC the last time the clocks
were updated */
TIME_T frame_duration; /**< one frame period (microseconds) */
MMAL_RATIONAL_T frame_rate; /**< frame rate set by the client */
uint32_t frame_rate_log2; /**< frame rate expressed as a power of two */
MMAL_RATIONAL_T scale; /**< current clock scale factor */
MMAL_BOOL_T pending_scale; /**< TRUE -> scale change is pending */
MMAL_CLOCK_LATENCY_T latency;
MMAL_CLOCK_UPDATE_THRESHOLD_T update_threshold;
MMAL_CLOCK_DISCONT_THRESHOLD_T discont_threshold;
MMAL_CLOCK_REQUEST_THRESHOLD_T request_threshold;
/** Clock port events */
struct
{
MMAL_LIST_T* queue; /**< pending events */
MMAL_LIST_T* free; /**< available event slots */
CLOCK_PORT_EVENT_T pool[MAX_CLOCK_EVENT_SLOTS];
} events;
} MMAL_COMPONENT_MODULE_T;
/** Clock port context */
typedef struct MMAL_PORT_MODULE_T
{
CLOCK_STREAM_T *stream; /**< stream associated with this clock port */
} MMAL_PORT_MODULE_T;
/*****************************************************************************/
/** Round x up to the next power of two */
static uint32_t next_pow2(uint32_t x)
{
x--;
x = (x >> 1) | x;
x = (x >> 2) | x;
x = (x >> 4) | x;
x = (x >> 8) | x;
x = (x >> 16) | x;
return ++x;
}
/** Given a power of 2 value, return the number of bit shifts */
static uint32_t pow2_shift(uint32_t x)
{
static const uint32_t BIT_POSITIONS[32] =
{
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
return BIT_POSITIONS[((x & -x) * 0x077CB531U) >> 27];
}
/** Add 2 values with saturation */
static inline TIME_T saturate_add(TIME_T a, TIME_T b)
{
TIME_T sum = a + b;
if (a > 0 && b > 0 && sum < 0)
sum = MAX_TIME;
else if (a < 0 && b < 0 && sum > 0)
sum = MIN_TIME;
return sum;
}
/*****************************************************************************/
/** Filter reset */
static void filter_init(FILTER_T *filter, uint32_t length)
{
memset(filter, 0, sizeof(*filter));
filter->last = length - 1;
filter->length = length;
};
/** Increment filter index modulo the length */
static inline uint32_t filter_index_wrap(uint32_t index, uint32_t length)
{
return (++index < length) ? index : 0;
}
/** Remove the oldest sample from the filter */
static void filter_drop(FILTER_T *filter)
{
if (!filter->count)
return;
filter->sum -= filter->h[filter->first];
filter->first = filter_index_wrap(filter->first, filter->length);
filter->count--;
}
/** Add a new sample (and drop the oldest when full) */
static void filter_insert(FILTER_T *filter, TIME_T sample)
{
if (filter->count == filter->length)
filter_drop(filter);
filter->last = filter_index_wrap(filter->last, filter->length);
filter->h[filter->last] = sample;
filter->sum = saturate_add(filter->sum, sample);
filter->count++;
}
/*****************************************************************************/
/** Create and initialise a clock stream */
static MMAL_BOOL_T clock_create_stream(CLOCK_STREAM_T **stream, uint32_t id, uint32_t filter_length)
{
CLOCK_STREAM_T *s = vcos_calloc(1, sizeof(CLOCK_STREAM_T), "clock stream");
if (!s)
{
LOG_ERROR("failed to allocate stream");
return MMAL_FALSE;
}
s->id = id;
filter_init(&s->avg_filter, filter_length);
filter_init(&s->std_filter, filter_length);
*stream = s;
return MMAL_TRUE;
}
/** Flag this stream as started */
static void clock_start_stream(CLOCK_STREAM_T *stream, TIME_T stc, TIME_T pts)
{
stream->started = MMAL_TRUE;
stream->pts = pts;
stream->stc = stc;
}
/** Reset the internal state of a stream */
static void clock_reset_stream(CLOCK_STREAM_T *stream)
{
if (!stream)
return;
stream->pts = 0;
stream->stc = 0;
stream->mt_off = 0;
stream->mt_off_avg = 0;
stream->mt_off_std = 0;
stream->started = MMAL_FALSE;
filter_init(&stream->avg_filter, stream->avg_filter.length);
filter_init(&stream->std_filter, stream->std_filter.length);
}
/** Update the internal state of a stream */
static CLOCK_STREAM_EVENT_T clock_update_stream(CLOCK_STREAM_T *stream, TIME_T stc, TIME_T pts,
TIME_T discont_threshold)
{
CLOCK_STREAM_EVENT_T event = CLOCK_STREAM_EVENT_NONE;
TIME_T pts_delta, stc_delta;
if (pts == MMAL_TIME_UNKNOWN)
{
LOG_TRACE("ignoring invalid timestamp received at %"PRIi64, stc);
return CLOCK_STREAM_EVENT_NONE;
}
if (!stream->started)
{
LOG_TRACE("stream %d started %"PRIi64" %"PRIi64, stream->id, stc, pts);
clock_start_stream(stream, stc, pts);
return CLOCK_STREAM_EVENT_STARTED;
}
/* XXX: This should really use the buffer flags to determine if a complete
* frame has been received. However, not all clients set MMAL buffer flags
* correctly (if at all). */
pts_delta = pts - stream->pts;
stc_delta = stc - stream->stc;
/* Check for discontinuities. */
if ((ABS(pts_delta) > discont_threshold) || (ABS(stc_delta) > discont_threshold))
{
LOG_ERROR("discontinuity detected on stream %d %"PRIi64" %"PRIi64" %"PRIi64,
stream->id, pts_delta, stc_delta, discont_threshold);
return CLOCK_STREAM_EVENT_DISCONT;
}
if (pts_delta)
{
/* A complete frame has now been received, so update the stream's notion of media time */
stream->mt_off = stream->pts - stream->stc;
filter_insert(&stream->avg_filter, stream->mt_off);
stream->mt_off_avg = stream->avg_filter.sum / stream->avg_filter.count;
filter_insert(&stream->std_filter, ABS(stream->mt_off - stream->mt_off_avg));
stream->mt_off_std = stream->std_filter.sum / stream->std_filter.count;
LOG_TRACE("stream %d %"PRIi64" %"PRIi64" %"PRIi64" %"PRIi64,
stream->id, stream->stc, stream->pts, stream->mt_off_avg, stream->mt_off);
event = CLOCK_STREAM_EVENT_FRAME_COMPLETE;
}
stream->pts = pts;
stream->stc = stc;
return event;
}
/*****************************************************************************/
/** Start all enabled clock ports, making sure all use the same thresholds */
static void clock_start_clocks(MMAL_COMPONENT_T *component, TIME_T media_time)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
unsigned i;
for (i = 0; i < component->clock_num; ++i)
{
MMAL_PORT_T *port = component->clock[i];
if (port->is_enabled)
{
LOG_TRACE("starting clock %d with time %"PRIi64, port->index, media_time);
mmal_port_clock_reference_set(port, MMAL_TRUE);
mmal_port_clock_media_time_set(port, media_time);
mmal_port_clock_update_threshold_set(port, &module->update_threshold);
mmal_port_clock_discont_threshold_set(port, &module->discont_threshold);
mmal_port_clock_request_threshold_set(port, &module->request_threshold);
mmal_port_clock_active_set(port, MMAL_TRUE);
}
}
}
/** Stop (and flush) all enabled clock ports */
static void clock_stop_clocks(MMAL_COMPONENT_T *component)
{
unsigned i;
for (i = 0; i < component->clock_num; ++i)
{
MMAL_PORT_T *port = component->clock[i];
if (port->is_enabled)
{
LOG_TRACE("stopping clock %d", port->index);
mmal_port_clock_request_flush(port);
mmal_port_clock_active_set(port, MMAL_FALSE);
}
}
}
/** Reset the internal state of all streams in order to rebase clock
* adjustment calculations */
static void clock_reset_clocks(MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
unsigned i;
for (i = 0; i < component->clock_num; ++i)
clock_reset_stream(component->clock[i]->priv->module->stream);
module->clock_discont = MMAL_TRUE;
}
/** Change the media-time for all enabled clock ports */
static void clock_set_media_time(MMAL_COMPONENT_T *component, TIME_T media_time)
{
unsigned i;
for (i = 0; i < component->clock_num; ++i)
{
MMAL_PORT_T *port = component->clock[i];
if (port->is_enabled)
mmal_port_clock_media_time_set(port, media_time);
}
}
/** Change the scale for all clock ports */
static void clock_set_scale(MMAL_COMPONENT_T *component, MMAL_RATIONAL_T scale)
{
unsigned i;
for (i = 0; i < component->clock_num; ++i)
mmal_port_clock_scale_set(component->clock[i], scale);
component->priv->module->pending_scale = 0;
}
/** Update the average and standard deviation calculations for all streams
* (dropping samples where necessary) and return the minimum and maximum
* streams */
static MMAL_BOOL_T clock_get_mt_off_avg(MMAL_COMPONENT_T *component, TIME_T stc,
CLOCK_STREAM_T **minimum, CLOCK_STREAM_T **maximum)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
TIME_T drop_threshold = 6 * module->frame_duration;
TIME_T reset_threshold = module->latency.target << 1;
TIME_T avg_min = MAX_TIME;
TIME_T avg_max = MIN_TIME;
TIME_T avg_bias;
TIME_T stc_delta;
unsigned i;
*minimum = 0;
*maximum = 0;
for (i = 0; i < component->clock_num; ++i)
{
CLOCK_STREAM_T *stream = component->clock[i]->priv->module->stream;
if (stream)
{
stc_delta = stc - stream->stc;
/* Drop samples from the moving average and standard deviation filters */
if (stc_delta > reset_threshold)
{
filter_init(&stream->avg_filter, stream->avg_filter.length);
filter_init(&stream->std_filter, stream->std_filter.length);
LOG_TRACE("reset stream %d filters due to stc_delta %"PRIi64, stream->id, stc_delta);
}
else if (stc_delta > drop_threshold)
{
filter_drop(&stream->avg_filter);
filter_drop(&stream->std_filter);
LOG_TRACE("drop stream %d filter samples due to stc_delta %"PRIi64, stream->id, stc_delta);
}
/* No point in continuing if filters are empty */
if (!stream->avg_filter.count)
continue;
/* Calculate new average and standard deviation for the stream */
stream->mt_off_avg = stream->avg_filter.sum / stream->avg_filter.count;
stream->mt_off_std = stream->std_filter.sum / stream->std_filter.count;
/* Select the minimum and maximum average between all active streams */
avg_bias = (stream->avg_filter.length - stream->avg_filter.count) * ABS(stream->mt_off_avg) / stream->avg_filter.length;
if ((stream->mt_off_avg + avg_bias) < avg_min)
{
avg_min = stream->mt_off_avg;
*minimum = stream;
LOG_TRACE("found min on %d mt_off_avg %"PRIi64" mt_off_std %"PRIi64" avg_bias %"PRIi64" count %d",
stream->id, stream->mt_off_avg, stream->mt_off_std, avg_bias, stream->avg_filter.count);
}
if ((stream->mt_off_avg - avg_bias) > avg_max)
{
avg_max = stream->mt_off_avg;
*maximum = stream;
LOG_TRACE("found max on %d mt_off_avg %"PRIi64" mt_off_std %"PRIi64" avg_bias %"PRIi64" count %d",
stream->id, stream->mt_off_avg, stream->mt_off_std, avg_bias, stream->avg_filter.count);
}
}
}
return (*minimum) && (*maximum);
}
/** Adjust the media-time of the playback clocks based on current timing statistics */
static void clock_adjust_clocks(MMAL_COMPONENT_T *component, TIME_T stc)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
CLOCK_STREAM_T *stream_min;
CLOCK_STREAM_T *stream_max;
TIME_T mt_off_clk;
TIME_T stc_prev;
if (!clock_get_mt_off_avg(component, stc, &stream_min, &stream_max))
return;
module->stream_min_id = stream_min->id;
module->stream_max_id = stream_max->id;
/* Calculate the actual media-time offset seen by the clock */
mt_off_clk = mmal_port_clock_media_time_get(component->clock[0]) - stc;
stc_prev = module->stc_at_update;
module->stc_at_update = stc;
/* If there has been a discontinuity, restart the clock,
* else use the clock control loop to apply a clock adjustment */
if (module->clock_discont)
{
module->clock_discont = MMAL_FALSE;
module->mt_off_clk = stream_min->mt_off_avg - module->latency.target;
module->mt_off_target = module->mt_off_clk;
clock_stop_clocks(component);
clock_start_clocks(component, module->mt_off_clk + stc);
}
else
{
/* Determine the new clock target */
TIME_T mt_off_target_max = stream_max->mt_off_avg - module->latency.target;
TIME_T mt_off_target_min = stream_min->mt_off_avg - module->frame_duration;
module->mt_off_target = MIN(mt_off_target_max, mt_off_target_min);
/* Calculate the proportional adjustment, capped by the attack rate
* set by the client */
TIME_T stc_delta = (stc > stc_prev) ? (stc - stc_prev) : 0;
TIME_T adj_p_max = stc_delta * module->latency.attack_rate / module->latency.attack_period;
module->adj_p = module->mt_off_target - module->mt_off_clk;
if (module->adj_p < -adj_p_max)
module->adj_p = -adj_p_max;
else if (module->adj_p > adj_p_max)
module->adj_p = adj_p_max;
/* Calculate the confidence of the adjustment using the approximate
* standard deviation for the selected stream:
*
* adj_m = 1.0 - STD * FPS / 4
*
* The adjustment factor is scaled up by 2^20 which is an approximation
* of 1000000 (microseconds per second) and the frame rate is assumed
* to be either 32 or 64 which are approximations for 24/25/30 and 60
* fps to avoid divisions. This has a lower limit of 0. */
module->adj_m =
MAX((1 << 20) - ((stream_min->mt_off_std << module->frame_rate_log2) >> 2), 0);
/* Modulate the proportional adjustment by the sample confidence
* and apply the adjustment to the current clock */
module->adj = (module->adj_p * module->adj_m) >> 20;
module->adj = (module->adj * (stream_min->avg_filter.count << 8) / stream_min->avg_filter.length) >> 8;
module->mt_off_clk += module->adj;
clock_set_media_time(component, module->mt_off_clk + stc);
}
/* Any pending clock scale changes can now be applied */
if (component->priv->module->pending_scale)
clock_set_scale(component, component->priv->module->scale);
}
/*****************************************************************************/
static void clock_process_stream_event(MMAL_COMPONENT_T *component, CLOCK_STREAM_T *stream,
CLOCK_STREAM_EVENT_T event, TIME_T stc)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
switch (event)
{
case CLOCK_STREAM_EVENT_FRAME_COMPLETE:
clock_adjust_clocks(component, stc);
if (clock_additional_logging)
{
VCOS_ALERT("STRM_%d %"PRIi64" %"PRIi64" %"PRIi64" %"PRIi64" %"PRIi64" %"PRIi64" %d %"
PRIi64" %"PRIi64" %"PRIi64" %"PRIi64" %"PRIi64" %u %u",
stream->id, stream->stc, stream->pts, stream->mt_off_avg, stream->mt_off,
stream->mt_off_std, ABS(stream->mt_off - stream->mt_off_avg), stream->avg_filter.count,
module->mt_off_clk, module->mt_off_target, module->adj_p, module->adj_m, module->adj,
module->stream_min_id, module->stream_max_id);
}
break;
case CLOCK_STREAM_EVENT_DISCONT:
clock_reset_clocks(component);
break;
default:
/* ignore all other events */
break;
}
}
/** Handler for input buffer events */
static void clock_process_input_buffer_info_event(MMAL_COMPONENT_T *component, MMAL_PORT_T *port,
const MMAL_CLOCK_BUFFER_INFO_T *info)
{
CLOCK_STREAM_EVENT_T stream_event = CLOCK_STREAM_EVENT_NONE;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_PORT_MODULE_T *port_module = port->priv->module;
TIME_T stc = (TIME_T)((uint64_t)info->arrival_time);
TIME_T pts = info->time_stamp;
LOG_TRACE("port %d %"PRIi64" %"PRIi64, port->index, stc, pts);
if (!port_module->stream)
{
/* First data received for this stream */
uint32_t filter_length = module->frame_rate.num * FILTER_DURATION /
module->frame_rate.den;
if (!clock_create_stream(&port_module->stream, port->index, filter_length))
return;
}
stream_event = clock_update_stream(port_module->stream, stc, pts, module->discont_threshold.threshold);
clock_process_stream_event(component, port_module->stream, stream_event, stc);
}
/** Handler for clock scale events */
static void clock_process_scale_event(MMAL_COMPONENT_T *component, MMAL_RATIONAL_T scale)
{
/* When pausing the clock (i.e. scale = 0.0), apply the scale change
* immediately. However, when resuming the clock (i.e. scale = 1.0),
* the scale change can only be applied the next time new buffer timing
* information is received. This ensures that clocks resume with the
* correct media-time. */
if (scale.num == 0)
{
component->priv->module->scale = scale;
clock_set_scale(component, scale);
}
else
{
/* Only support scale == 1.0 */
if (!mmal_rational_equal(component->priv->module->scale, scale) &&
(scale.num == scale.den))
{
component->priv->module->scale = scale;
component->priv->module->pending_scale = 1;
clock_reset_clocks(component);
}
}
}
/** Handler for update threshold events */
static void clock_process_update_threshold_event(MMAL_COMPONENT_T *component, const MMAL_CLOCK_UPDATE_THRESHOLD_T *threshold)
{
unsigned i;
component->priv->module->update_threshold = *threshold;
for (i = 0; i < component->clock_num; ++i)
mmal_port_clock_update_threshold_set(component->clock[i], threshold);
}
/** Handler for discontinuity threshold events */
static void clock_process_discont_threshold_event(MMAL_COMPONENT_T *component, const MMAL_CLOCK_DISCONT_THRESHOLD_T *threshold)
{
unsigned i;
component->priv->module->discont_threshold = *threshold;
for (i = 0; i < component->clock_num; ++i)
mmal_port_clock_discont_threshold_set(component->clock[i], threshold);
}
/** Handler for request threshold events */
static void clock_process_request_threshold_event(MMAL_COMPONENT_T *component, const MMAL_CLOCK_REQUEST_THRESHOLD_T *threshold)
{
unsigned i;
component->priv->module->request_threshold = *threshold;
for (i = 0; i < component->clock_num; ++i)
mmal_port_clock_request_threshold_set(component->clock[i], threshold);
}
/** Handler for latency events */
static void clock_process_latency_event(MMAL_COMPONENT_T *component, const MMAL_CLOCK_LATENCY_T *latency)
{
component->priv->module->latency = *latency;
clock_reset_clocks(component);
}
/** Add a clock port event to the queue and trigger the action thread */
static MMAL_STATUS_T clock_event_queue(MMAL_COMPONENT_T *component, MMAL_PORT_T *port, const MMAL_CLOCK_EVENT_T *event)
{
CLOCK_PORT_EVENT_T *slot = (CLOCK_PORT_EVENT_T*)mmal_list_pop_front(component->priv->module->events.free);
if (!slot)
{
LOG_ERROR("no event slots available");
return MMAL_ENOSPC;
}
slot->port = port;
slot->event = *event;
mmal_list_push_back(component->priv->module->events.queue, &slot->link);
return mmal_component_action_trigger(component);
}
/** Get the next clock port event in the queue */
static MMAL_STATUS_T clock_event_dequeue(MMAL_COMPONENT_T *component, CLOCK_PORT_EVENT_T *port_event)
{
CLOCK_PORT_EVENT_T *slot = (CLOCK_PORT_EVENT_T*)mmal_list_pop_front(component->priv->module->events.queue);
if (!slot)
return MMAL_EINVAL;
port_event->port = slot->port;
port_event->event = slot->event;
mmal_list_push_back(component->priv->module->events.free, &slot->link);
if (port_event->event.buffer)
{
port_event->event.buffer->length = 0;
mmal_port_buffer_header_callback(port_event->port, port_event->event.buffer);
}
return MMAL_SUCCESS;
}
/** Event callback from a clock port */
static void clock_event_cb(MMAL_PORT_T *port, const MMAL_CLOCK_EVENT_T *event)
{
clock_event_queue(port->component, port, event);
}
/*****************************************************************************/
/** Actual processing function */
static MMAL_BOOL_T clock_do_processing(MMAL_COMPONENT_T *component)
{
CLOCK_PORT_EVENT_T port_event;
if (clock_event_dequeue(component, &port_event) != MMAL_SUCCESS)
return MMAL_FALSE; /* No more external events to process */
/* Process external events (coming from clock ports) */
switch (port_event.event.id)
{
case MMAL_CLOCK_EVENT_SCALE:
clock_process_scale_event(component, port_event.event.data.scale);
break;
case MMAL_CLOCK_EVENT_UPDATE_THRESHOLD:
clock_process_update_threshold_event(component, &port_event.event.data.update_threshold);
break;
case MMAL_CLOCK_EVENT_DISCONT_THRESHOLD:
clock_process_discont_threshold_event(component, &port_event.event.data.discont_threshold);
break;
case MMAL_CLOCK_EVENT_REQUEST_THRESHOLD:
clock_process_request_threshold_event(component, &port_event.event.data.request_threshold);
break;
case MMAL_CLOCK_EVENT_LATENCY:
clock_process_latency_event(component, &port_event.event.data.latency);
break;
case MMAL_CLOCK_EVENT_INPUT_BUFFER_INFO:
clock_process_input_buffer_info_event(component, port_event.port, &port_event.event.data.buffer);
break;
default:
break;
}
return MMAL_TRUE;
}
/** Component action thread */
static void clock_do_processing_loop(MMAL_COMPONENT_T *component)
{
while (clock_do_processing(component));
}
/*****************************************************************************/
/** Set a parameter on the clock component's control port */
static MMAL_STATUS_T clock_control_parameter_set(MMAL_PORT_T *port, const MMAL_PARAMETER_HEADER_T *param)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_STATUS_T status = MMAL_SUCCESS;
switch (param->id)
{
case MMAL_PARAMETER_CLOCK_FRAME_RATE:
{
const MMAL_PARAMETER_FRAME_RATE_T *p = (const MMAL_PARAMETER_FRAME_RATE_T *)param;
module->frame_rate = p->frame_rate;
/* XXX: take frame_rate.den into account */
module->frame_rate_log2 = pow2_shift(next_pow2(module->frame_rate.num));
module->frame_duration = p->frame_rate.den * 1000000 / p->frame_rate.num;
LOG_TRACE("frame rate %d/%d (%u) duration %"PRIi64,
module->frame_rate.num, module->frame_rate.den,
module->frame_rate_log2, module->frame_duration);
}
break;
case MMAL_PARAMETER_CLOCK_LATENCY:
{
/* Changing the latency setting requires a reset of the clock algorithm, but
* that can only be safely done from within the component's worker thread.
* So, queue the new latency setting as a clock event. */
const MMAL_PARAMETER_CLOCK_LATENCY_T *p = (const MMAL_PARAMETER_CLOCK_LATENCY_T *)param;
MMAL_CLOCK_EVENT_T event = { MMAL_CLOCK_EVENT_LATENCY, MMAL_CLOCK_EVENT_MAGIC };
LOG_TRACE("latency target %"PRIi64" attack %"PRIi64"/%"PRIi64,
p->value.target, p->value.attack_rate, p->value.attack_period);
event.data.latency = p->value;
status = clock_event_queue(port->component, port, &event);
}
break;
default:
LOG_ERROR("parameter not supported (0x%x)", param->id);
status = MMAL_ENOSYS;
break;
}
return status;
}
/** Destroy a previously created component */
static MMAL_STATUS_T clock_component_destroy(MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
unsigned int i;
if (module->events.free)
mmal_list_destroy(module->events.free);
if (module->events.queue)
mmal_list_destroy(module->events.queue);
if (component->clock_num)
{
for (i = 0; i < component->clock_num; ++i)
vcos_free(component->clock[i]->priv->module->stream);
mmal_ports_clock_free(component->clock, component->clock_num);
}
vcos_free(module);
return MMAL_SUCCESS;
}
/** Create an instance of a clock component */
static MMAL_STATUS_T mmal_component_create_clock(const char *name, MMAL_COMPONENT_T *component)
{
int i;
MMAL_COMPONENT_MODULE_T *module;
MMAL_STATUS_T status = MMAL_ENOMEM;
MMAL_PARAM_UNUSED(name);
/* Allocate the context for our module */
component->priv->module = module = vcos_malloc(sizeof(*module), "mmal module");
if (!module)
return MMAL_ENOMEM;
memset(module, 0, sizeof(*module));
component->priv->pf_destroy = clock_component_destroy;
/* Create the clock ports (clock ports are managed by the framework) */
component->clock = mmal_ports_clock_alloc(component, CLOCK_PORTS_NUM,
sizeof(MMAL_PORT_MODULE_T), clock_event_cb);
if (!component->clock)
goto error;
component->clock_num = CLOCK_PORTS_NUM;
component->control->priv->pf_parameter_set = clock_control_parameter_set;
/* Setup event slots */
module->events.free = mmal_list_create();
module->events.queue = mmal_list_create();
if (!module->events.free || !module->events.queue)
{
LOG_ERROR("failed to create list %p %p", module->events.free, module->events.queue);
goto error;
}
for (i = 0; i < MAX_CLOCK_EVENT_SLOTS; ++i)
mmal_list_push_back(module->events.free, &module->events.pool[i].link);
component->priv->priority = VCOS_THREAD_PRI_REALTIME;
status = mmal_component_action_register(component, clock_do_processing_loop);
module->clock_discont = MMAL_TRUE;
module->frame_rate.num = DEFAULT_FRAME_RATE;
module->frame_rate.den = 1;
module->scale = mmal_port_clock_scale_get(component->clock[0]);
memset(&module->latency, 0, sizeof(module->latency));
module->latency.target = DEFAULT_CLOCK_LATENCY;
mmal_port_clock_update_threshold_get(component->clock[0], &module->update_threshold);
mmal_port_clock_discont_threshold_get(component->clock[0], &module->discont_threshold);
mmal_port_clock_request_threshold_get(component->clock[0], &module->request_threshold);
return status;
error:
clock_component_destroy(component);
return status;
}
/*****************************************************************************/
MMAL_CONSTRUCTOR(mmal_register_component_clock);
void mmal_register_component_clock(void)
{
mmal_component_supplier_register("clock", mmal_component_create_clock);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,327 @@
/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "mmal.h"
#include "core/mmal_component_private.h"
#include "core/mmal_port_private.h"
#include "mmal_logging.h"
/*****************************************************************************/
typedef struct MMAL_COMPONENT_MODULE_T
{
MMAL_STATUS_T status; /**< current status of the component */
} MMAL_COMPONENT_MODULE_T;
typedef struct MMAL_PORT_MODULE_T
{
MMAL_QUEUE_T *queue; /**< queue for the buffers sent to the ports */
MMAL_BOOL_T needs_configuring; /**< port is waiting for a format commit */
} MMAL_PORT_MODULE_T;
/*****************************************************************************/
/** Actual processing function */
static MMAL_BOOL_T copy_do_processing(MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_PORT_T *port_in = component->input[0];
MMAL_PORT_T *port_out = component->output[0];
MMAL_BUFFER_HEADER_T *in, *out;
if (port_out->priv->module->needs_configuring)
return 0;
in = mmal_queue_get(port_in->priv->module->queue);
if (!in)
return 0;
/* Handle event buffers */
if (in->cmd)
{
MMAL_EVENT_FORMAT_CHANGED_T *event = mmal_event_format_changed_get(in);
if (event)
{
module->status = mmal_format_full_copy(port_in->format, event->format);
if (module->status == MMAL_SUCCESS)
module->status = port_in->priv->pf_set_format(port_in);
if (module->status != MMAL_SUCCESS)
{
LOG_ERROR("format not set on port %s %p (%i)", port_in->name, port_in, module->status);
if (mmal_event_error_send(component, module->status) != MMAL_SUCCESS)
LOG_ERROR("unable to send an error event buffer");
}
}
else
{
LOG_ERROR("discarding event %i on port %s %p", (int)in->cmd, port_in->name, port_in);
}
in->length = 0;
mmal_port_buffer_header_callback(port_in, in);
return 1;
}
/* Don't do anything if we've already seen an error */
if (module->status != MMAL_SUCCESS)
{
mmal_queue_put_back(port_in->priv->module->queue, in);
return 0;
}
out = mmal_queue_get(port_out->priv->module->queue);
if (!out)
{
mmal_queue_put_back(port_in->priv->module->queue, in);
return 0;
}
/* Sanity check the output buffer is big enough */
if (out->alloc_size < in->length)
{
module->status = MMAL_EINVAL;
if (mmal_event_error_send(component, module->status) != MMAL_SUCCESS)
LOG_ERROR("unable to send an error event buffer");
return 0;
}
mmal_buffer_header_mem_lock(out);
mmal_buffer_header_mem_lock(in);
memcpy(out->data, in->data + in->offset, in->length);
mmal_buffer_header_mem_unlock(in);
mmal_buffer_header_mem_unlock(out);
out->length = in->length;
out->offset = 0;
out->flags = in->flags;
out->pts = in->pts;
out->dts = in->dts;
*out->type = *in->type;
/* Send buffers back */
in->length = 0;
mmal_port_buffer_header_callback(port_in, in);
mmal_port_buffer_header_callback(port_out, out);
return 1;
}
/*****************************************************************************/
static void copy_do_processing_loop(MMAL_COMPONENT_T *component)
{
while (copy_do_processing(component));
}
/** Destroy a previously created component */
static MMAL_STATUS_T copy_component_destroy(MMAL_COMPONENT_T *component)
{
unsigned int i;
for(i = 0; i < component->input_num; i++)
if(component->input[i]->priv->module->queue)
mmal_queue_destroy(component->input[i]->priv->module->queue);
if(component->input_num)
mmal_ports_free(component->input, component->input_num);
for(i = 0; i < component->output_num; i++)
if(component->output[i]->priv->module->queue)
mmal_queue_destroy(component->output[i]->priv->module->queue);
if(component->output_num)
mmal_ports_free(component->output, component->output_num);
vcos_free(component->priv->module);
return MMAL_SUCCESS;
}
/** Enable processing on a port */
static MMAL_STATUS_T copy_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
{
MMAL_PARAM_UNUSED(cb);
/* We need to propagate the buffer requirements when the input port is
* enabled */
if (port->type == MMAL_PORT_TYPE_INPUT)
return port->priv->pf_set_format(port);
return MMAL_SUCCESS;
}
/** Flush a port */
static MMAL_STATUS_T copy_port_flush(MMAL_PORT_T *port)
{
MMAL_PORT_MODULE_T *port_module = port->priv->module;
MMAL_BUFFER_HEADER_T *buffer;
/* Flush buffers that our component is holding on to */
buffer = mmal_queue_get(port_module->queue);
while(buffer)
{
mmal_port_buffer_header_callback(port, buffer);
buffer = mmal_queue_get(port_module->queue);
}
return MMAL_SUCCESS;
}
/** Disable processing on a port */
static MMAL_STATUS_T copy_port_disable(MMAL_PORT_T *port)
{
/* We just need to flush our internal queue */
return copy_port_flush(port);
}
/** Send a buffer header to a port */
static MMAL_STATUS_T copy_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
mmal_queue_put(port->priv->module->queue, buffer);
mmal_component_action_trigger(port->component);
return MMAL_SUCCESS;
}
/** Set format on input port */
static MMAL_STATUS_T copy_input_port_format_commit(MMAL_PORT_T *in)
{
MMAL_COMPONENT_T *component = in->component;
MMAL_PORT_T *out = component->output[0];
MMAL_EVENT_FORMAT_CHANGED_T *event;
MMAL_BUFFER_HEADER_T *buffer;
MMAL_STATUS_T status;
/* Check if there's anything to propagate to the output port */
/* The format of the output port needs to match the input port */
if (!mmal_format_compare(in->format, out->format) &&
out->buffer_size_min == out->buffer_size_recommended &&
out->buffer_size_min == MMAL_MAX(in->buffer_size_min, in->buffer_size))
return MMAL_SUCCESS;
/* If the output port is not enabled we just need to update its format.
* Otherwise we'll have to trigger a format changed event for it. */
if (!out->is_enabled)
{
out->buffer_size_min = out->buffer_size_recommended =
MMAL_MAX(in->buffer_size, in->buffer_size_min);
return mmal_format_full_copy(out->format, in->format);
}
/* Send an event on the output port */
status = mmal_port_event_get(out, &buffer, MMAL_EVENT_FORMAT_CHANGED);
if (status != MMAL_SUCCESS)
{
LOG_ERROR("unable to get an event buffer");
return status;
}
event = mmal_event_format_changed_get(buffer);
mmal_format_copy(event->format, in->format); /* FIXME: can full copy be done ? */
/* Pass on the buffer requirements */
event->buffer_num_min = out->buffer_num_min;
event->buffer_num_recommended = out->buffer_num_recommended;
event->buffer_size_min = event->buffer_size_recommended =
MMAL_MAX(in->buffer_size_min, in->buffer_size);
out->priv->module->needs_configuring = 1;
mmal_port_event_send(out, buffer);
return status;
}
/** Set format on output port */
static MMAL_STATUS_T copy_output_port_format_commit(MMAL_PORT_T *out)
{
MMAL_COMPONENT_T *component = out->component;
MMAL_PORT_T *in = component->input[0];
/* The format of the output port needs to match the input port */
if (mmal_format_compare(out->format, in->format))
return MMAL_EINVAL;
out->priv->module->needs_configuring = 0;
mmal_component_action_trigger(out->component);
return MMAL_SUCCESS;
}
/** Create an instance of a component */
static MMAL_STATUS_T mmal_component_create_copy(const char *name, MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module;
MMAL_STATUS_T status = MMAL_ENOMEM;
MMAL_PARAM_UNUSED(name);
/* Allocate the context for our module */
component->priv->module = module = vcos_malloc(sizeof(*module), "mmal module");
if (!module)
return MMAL_ENOMEM;
memset(module, 0, sizeof(*module));
component->priv->pf_destroy = copy_component_destroy;
/* Allocate and initialise all the ports for this component */
component->input = mmal_ports_alloc(component, 1, MMAL_PORT_TYPE_INPUT, sizeof(MMAL_PORT_MODULE_T));
if(!component->input)
goto error;
component->input_num = 1;
component->input[0]->priv->pf_enable = copy_port_enable;
component->input[0]->priv->pf_disable = copy_port_disable;
component->input[0]->priv->pf_flush = copy_port_flush;
component->input[0]->priv->pf_send = copy_port_send;
component->input[0]->priv->pf_set_format = copy_input_port_format_commit;
component->input[0]->buffer_num_min = 1;
component->input[0]->buffer_num_recommended = 0;
component->input[0]->priv->module->queue = mmal_queue_create();
if(!component->input[0]->priv->module->queue)
goto error;
component->output = mmal_ports_alloc(component, 1, MMAL_PORT_TYPE_OUTPUT, sizeof(MMAL_PORT_MODULE_T));
if(!component->output)
goto error;
component->output_num = 1;
component->output[0]->priv->pf_enable = copy_port_enable;
component->output[0]->priv->pf_disable = copy_port_disable;
component->output[0]->priv->pf_flush = copy_port_flush;
component->output[0]->priv->pf_send = copy_port_send;
component->output[0]->priv->pf_set_format = copy_output_port_format_commit;
component->output[0]->buffer_num_min = 1;
component->output[0]->buffer_num_recommended = 0;
component->output[0]->priv->module->queue = mmal_queue_create();
if(!component->output[0]->priv->module->queue)
goto error;
status = mmal_component_action_register(component, copy_do_processing_loop);
if (status != MMAL_SUCCESS)
goto error;
return MMAL_SUCCESS;
error:
copy_component_destroy(component);
return status;
}
MMAL_CONSTRUCTOR(mmal_register_component_copy);
void mmal_register_component_copy(void)
{
mmal_component_supplier_register("copy", mmal_component_create_copy);
}
@@ -0,0 +1,125 @@
/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "mmal.h"
#include "core/mmal_component_private.h"
#include "core/mmal_port_private.h"
#include "mmal_logging.h"
#define NULLSINK_PORTS_NUM 1
/** Destroy a previously created component */
static MMAL_STATUS_T null_sink_component_destroy(MMAL_COMPONENT_T *component)
{
if(component->input_num)
mmal_ports_free(component->input, component->input_num);
return MMAL_SUCCESS;
}
/** Enable processing on a port */
static MMAL_STATUS_T null_sink_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
{
MMAL_PARAM_UNUSED(port);
MMAL_PARAM_UNUSED(cb);
return MMAL_SUCCESS;
}
/** Flush a port */
static MMAL_STATUS_T null_sink_port_flush(MMAL_PORT_T *port)
{
MMAL_PARAM_UNUSED(port);
return MMAL_SUCCESS;
}
/** Disable processing on a port */
static MMAL_STATUS_T null_sink_port_disable(MMAL_PORT_T *port)
{
MMAL_PARAM_UNUSED(port);
return MMAL_SUCCESS;
}
/** Send a buffer header to a port */
static MMAL_STATUS_T null_sink_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
MMAL_BOOL_T eos = buffer->flags & MMAL_BUFFER_HEADER_FLAG_EOS;
/* Send buffer back */
buffer->length = 0;
mmal_port_buffer_header_callback(port, buffer);
/* Generate EOS events */
if(eos)
return mmal_event_eos_send(port);
return MMAL_SUCCESS;
}
/** Set format on a port */
static MMAL_STATUS_T null_sink_port_format_commit(MMAL_PORT_T *port)
{
MMAL_PARAM_UNUSED(port);
return MMAL_SUCCESS;
}
/** Create an instance of a component */
static MMAL_STATUS_T mmal_component_create_null_sink(const char *name, MMAL_COMPONENT_T *component)
{
MMAL_STATUS_T status = MMAL_ENOMEM;
unsigned int i;
MMAL_PARAM_UNUSED(name);
component->priv->pf_destroy = null_sink_component_destroy;
/* Allocate all the ports for this component */
component->input = mmal_ports_alloc(component, NULLSINK_PORTS_NUM, MMAL_PORT_TYPE_INPUT, 0);
if(!component->input)
goto error;
component->input_num = NULLSINK_PORTS_NUM;
for(i = 0; i < component->input_num; i++)
{
component->input[i]->priv->pf_enable = null_sink_port_enable;
component->input[i]->priv->pf_disable = null_sink_port_disable;
component->input[i]->priv->pf_flush = null_sink_port_flush;
component->input[i]->priv->pf_send = null_sink_port_send;
component->input[i]->priv->pf_set_format = null_sink_port_format_commit;
component->input[i]->buffer_num_min = 1;
component->input[i]->buffer_num_recommended = 1;
}
return MMAL_SUCCESS;
error:
null_sink_component_destroy(component);
return status;
}
MMAL_CONSTRUCTOR(mmal_register_component_null_sink);
void mmal_register_component_null_sink(void)
{
mmal_component_supplier_register("null_sink", mmal_component_create_null_sink);
}
@@ -0,0 +1,284 @@
/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "mmal.h"
#include "core/mmal_component_private.h"
#include "core/mmal_port_private.h"
#include "mmal_logging.h"
#define PASSTHROUGH_PORTS_NUM 1
/*****************************************************************************/
typedef struct MMAL_COMPONENT_MODULE_T
{
MMAL_BOOL_T error; /**< Error state */
} MMAL_COMPONENT_MODULE_T;
typedef struct MMAL_PORT_MODULE_T
{
MMAL_QUEUE_T *queue; /**< queue for the buffers sent to the ports */
} MMAL_PORT_MODULE_T;
/*****************************************************************************/
/** Destroy a previously created component */
static MMAL_STATUS_T passthrough_component_destroy(MMAL_COMPONENT_T *component)
{
unsigned int i;
for(i = 0; i < component->input_num; i++)
if(component->input[i]->priv->module->queue)
mmal_queue_destroy(component->input[i]->priv->module->queue);
if(component->input_num)
mmal_ports_free(component->input, component->input_num);
for(i = 0; i < component->output_num; i++)
if(component->output[i]->priv->module->queue)
mmal_queue_destroy(component->output[i]->priv->module->queue);
if(component->output_num)
mmal_ports_free(component->output, component->output_num);
vcos_free(component->priv->module);
return MMAL_SUCCESS;
}
/** Enable processing on a port */
static MMAL_STATUS_T passthrough_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
{
MMAL_PARAM_UNUSED(port);
MMAL_PARAM_UNUSED(cb);
return MMAL_SUCCESS;
}
/** Flush a port */
static MMAL_STATUS_T passthrough_port_flush(MMAL_PORT_T *port)
{
MMAL_PORT_MODULE_T *port_module = port->priv->module;
MMAL_BUFFER_HEADER_T *buffer;
/* Flush buffers that our component is holding on to */
buffer = mmal_queue_get(port_module->queue);
while(buffer)
{
mmal_port_buffer_header_callback(port, buffer);
buffer = mmal_queue_get(port_module->queue);
}
return MMAL_SUCCESS;
}
/** Disable processing on a port */
static MMAL_STATUS_T passthrough_port_disable(MMAL_PORT_T *port)
{
/* We just need to flush our internal queue */
return passthrough_port_flush(port);
}
/** Send a buffer header to a port */
static MMAL_STATUS_T passthrough_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_PORT_T **other_port, *in_port, *out_port;
MMAL_BUFFER_HEADER_T **other_buffer, *in = 0, *out = 0;
MMAL_STATUS_T status;
if (module->error)
{
mmal_queue_put(port->priv->module->queue, buffer);
return MMAL_SUCCESS; /* Just do nothing */
}
in_port = port->component->input[port->index];
out_port = port->component->output[port->index];
if (port->type == MMAL_PORT_TYPE_INPUT)
{
other_port = &out_port;
other_buffer = &out;
in = buffer;
}
else
{
other_port = &in_port;
other_buffer = &in;
out = buffer;
}
/* Get a buffer header from the matching port */
*other_buffer = mmal_queue_get((*other_port)->priv->module->queue);
if (!*other_buffer)
{
/* None available. Just queue the buffer header for now. */
mmal_queue_put(port->priv->module->queue, buffer);
return MMAL_SUCCESS;
}
/* Copy our input buffer header */
status = mmal_buffer_header_replicate(out, in);
if (status != MMAL_SUCCESS)
goto error;
/* Consume the input buffer */
in->length = 0;
/* Send buffers back */
mmal_port_buffer_header_callback(in_port, in);
mmal_port_buffer_header_callback(out_port, out);
return MMAL_SUCCESS;
error:
mmal_queue_put(in_port->priv->module->queue, in);
mmal_queue_put(out_port->priv->module->queue, out);
status = mmal_event_error_send(port->component, status);
if (status != MMAL_SUCCESS)
{
LOG_ERROR("unable to send an error event buffer (%i)", (int)status);
return MMAL_SUCCESS;
}
module->error = 1;
return MMAL_SUCCESS;
}
/** Set format on a port */
static MMAL_STATUS_T passthrough_port_format_commit(MMAL_PORT_T *port)
{
/* Sanity check */
if (port->type == MMAL_PORT_TYPE_OUTPUT)
{
LOG_ERROR("output port is read-only");
return MMAL_EINVAL;
}
return mmal_format_full_copy(port->component->output[port->index]->format, port->format);
}
static MMAL_STATUS_T passthrough_port_parameter_set(MMAL_PORT_T *port, const MMAL_PARAMETER_HEADER_T *param)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_PORT_T *in = component->input[port->index], *out = component->input[port->index];
switch (param->id)
{
case MMAL_PARAMETER_BUFFER_REQUIREMENTS:
{
/* Propagate the requirements to the matching input and output the ports */
const MMAL_PARAMETER_BUFFER_REQUIREMENTS_T *req = (const MMAL_PARAMETER_BUFFER_REQUIREMENTS_T *)param;
uint32_t buffer_num_min = MMAL_MAX(port->buffer_num_min, req->buffer_num_min);
uint32_t buffer_num_recommended = MMAL_MAX(port->buffer_num_recommended, req->buffer_num_recommended);
uint32_t buffer_size_min = MMAL_MAX(port->buffer_size_min, req->buffer_size_min);
uint32_t buffer_size_recommended = MMAL_MAX(port->buffer_size_recommended, req->buffer_size_recommended);
in->buffer_num_min = buffer_num_min;
in->buffer_num_recommended = buffer_num_recommended;
in->buffer_size_min = buffer_size_min;
in->buffer_size_recommended = buffer_size_recommended;
out->buffer_num_min = buffer_num_min;
out->buffer_num_recommended = buffer_num_recommended;
out->buffer_size_min = buffer_size_min;
out->buffer_size_recommended = buffer_size_recommended;
}
return MMAL_SUCCESS;
default:
return MMAL_ENOSYS;
}
}
/** Create an instance of a component */
static MMAL_STATUS_T mmal_component_create_passthrough(const char *name, MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module;
MMAL_STATUS_T status = MMAL_ENOMEM;
unsigned int i;
MMAL_PARAM_UNUSED(name);
/* Allocate the context for our module */
component->priv->module = module = vcos_malloc(sizeof(*module), "mmal module");
if (!module)
return MMAL_ENOMEM;
memset(module, 0, sizeof(*module));
component->priv->pf_destroy = passthrough_component_destroy;
/* Allocate and initialise all the ports for this component */
component->input = mmal_ports_alloc(component, PASSTHROUGH_PORTS_NUM,
MMAL_PORT_TYPE_INPUT, sizeof(MMAL_PORT_MODULE_T));
if(!component->input)
goto error;
component->input_num = PASSTHROUGH_PORTS_NUM;
for(i = 0; i < component->input_num; i++)
{
component->input[i]->priv->pf_enable = passthrough_port_enable;
component->input[i]->priv->pf_disable = passthrough_port_disable;
component->input[i]->priv->pf_flush = passthrough_port_flush;
component->input[i]->priv->pf_send = passthrough_port_send;
component->input[i]->priv->pf_set_format = passthrough_port_format_commit;
component->input[i]->priv->pf_parameter_set = passthrough_port_parameter_set;
component->input[i]->buffer_num_min = 1;
component->input[i]->buffer_num_recommended = 0;
component->input[i]->priv->module->queue = mmal_queue_create();
if(!component->input[i]->priv->module->queue)
goto error;
}
component->output = mmal_ports_alloc(component, PASSTHROUGH_PORTS_NUM,
MMAL_PORT_TYPE_OUTPUT, sizeof(MMAL_PORT_MODULE_T));
if(!component->output)
goto error;
component->output_num = PASSTHROUGH_PORTS_NUM;
for(i = 0; i < component->output_num; i++)
{
component->output[i]->priv->pf_enable = passthrough_port_enable;
component->output[i]->priv->pf_disable = passthrough_port_disable;
component->output[i]->priv->pf_flush = passthrough_port_flush;
component->output[i]->priv->pf_send = passthrough_port_send;
component->output[i]->priv->pf_set_format = passthrough_port_format_commit;
component->output[i]->priv->pf_parameter_set = passthrough_port_parameter_set;
component->output[i]->buffer_num_min = 1;
component->output[i]->buffer_num_recommended = 0;
component->output[i]->capabilities = MMAL_PORT_CAPABILITY_PASSTHROUGH;
component->output[i]->priv->module->queue = mmal_queue_create();
if(!component->output[i]->priv->module->queue)
goto error;
}
return MMAL_SUCCESS;
error:
passthrough_component_destroy(component);
return status;
}
MMAL_CONSTRUCTOR(mmal_register_component_passthrough);
void mmal_register_component_passthrough(void)
{
mmal_component_supplier_register("passthrough", mmal_component_create_passthrough);
}
@@ -0,0 +1,485 @@
/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "mmal.h"
#include "mmal_logging.h"
#include "core/mmal_port_private.h"
#include "core/mmal_component_private.h"
#include "core/mmal_clock_private.h"
#define SCHEDULER_CLOCK_PORTS_NUM 1
#define SCHEDULER_INPUT_PORTS_NUM 1
#define SCHEDULER_OUTPUT_PORTS_NUM 1
#define SCHEDULER_REQUEST_SLOTS 16
/*****************************************************************************/
typedef struct MMAL_COMPONENT_MODULE_T
{
MMAL_STATUS_T status; /**< current status of the component */
} MMAL_COMPONENT_MODULE_T;
typedef struct MMAL_PORT_MODULE_T
{
MMAL_QUEUE_T *queue; /**< queue for the buffers sent to the port */
int64_t last_ts; /***< Last timestamp seen on the input port */
} MMAL_PORT_MODULE_T;
/*****************************************************************************/
/** Process an event buffer */
static MMAL_STATUS_T scheduler_event_process(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
MMAL_STATUS_T status = MMAL_EINVAL;
if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED)
{
MMAL_EVENT_FORMAT_CHANGED_T *event =
mmal_event_format_changed_get(buffer);
if (!event)
goto end;
status = mmal_format_full_copy(port->format, event->format);
if (status == MMAL_SUCCESS)
status = mmal_port_format_commit(port);
if (status != MMAL_SUCCESS)
{
LOG_ERROR("format commit failed on port %s (%i)",
port->name, status);
goto end;
}
status = MMAL_SUCCESS;
}
/* Forward any other event as is to the next component */
else
{
LOG_DEBUG("forwarding unknown event %4.4s", (char *)&buffer->cmd);
status = mmal_event_forward(buffer, port->component->output[port->index]);
if (status != MMAL_SUCCESS)
{
LOG_ERROR("unable to forward event %4.4s", (char *)&buffer->cmd);
goto end;
}
}
end:
buffer->length = 0;
mmal_port_buffer_header_callback(port, buffer);
return status;
}
/** Invoked when a clock request has been serviced */
static void scheduler_component_clock_port_request_cb(MMAL_PORT_T *port, int64_t media_time, void *cb_data)
{
MMAL_COMPONENT_T *component = port->component;;
MMAL_PORT_T *port_in = component->input[0];
MMAL_PORT_T *port_out = component->output[0];
MMAL_BUFFER_HEADER_T *buffer = (MMAL_BUFFER_HEADER_T*)cb_data;
LOG_TRACE("media-time %"PRIi64" pts %"PRIi64" delta %"PRIi64,
media_time, buffer->pts, media_time - buffer->pts);
if (buffer->cmd)
scheduler_event_process(port_in, buffer);
else
/* Forward the buffer to the next component */
mmal_port_buffer_header_callback(port_out, buffer);
}
/** Process buffers on the input and output ports */
static MMAL_BOOL_T scheduler_component_process_buffers(MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_PORT_T *port_in = component->input[0];
MMAL_PORT_T *port_out = component->output[0];
MMAL_QUEUE_T *queue_in = port_in->priv->module->queue;
MMAL_QUEUE_T *queue_out = port_out->priv->module->queue;
MMAL_BUFFER_HEADER_T *in, *out;
MMAL_STATUS_T cb_status = MMAL_EINVAL;
/* Don't do anything if we've already seen an error */
if (module->status != MMAL_SUCCESS)
{
LOG_ERROR("module failure");
return MMAL_FALSE;
}
in = mmal_queue_get(queue_in);
/* Special case for dealing with event buffers */
if (in && in->cmd)
{
/* We normally schedule cmds so they come out in the right order,
* except when we don't know when to schedule them, which will only
* happen at the start of the stream.
* The fudge factor added to the last timestamp here is because the
* cmd really applies to the next buffer so we want to make sure
* we leave enough time to the next component to process the previous
* buffer before forwarding the event. */
in->pts = port_in->priv->module->last_ts + 1000;
if (in->pts != MMAL_TIME_UNKNOWN)
cb_status = mmal_port_clock_request_add(component->clock[0],
in->pts, scheduler_component_clock_port_request_cb, in);
if (cb_status != MMAL_SUCCESS)
{
if (in->pts != MMAL_TIME_UNKNOWN)
LOG_ERROR("failed to add request for cmd");
scheduler_event_process(port_in, in);
}
return MMAL_TRUE;
}
/* Need both an input and output buffer to be able to go any further */
out = mmal_queue_get(queue_out);
if (!in || !out)
goto end;
if (port_out->capabilities & MMAL_PORT_CAPABILITY_PASSTHROUGH)
{
/* Just need to keep a reference to the input buffer */
module->status = mmal_buffer_header_replicate(out, in);
}
else
{
/* Make a full copy of the input payload */
if (out->alloc_size < in->length)
{
LOG_ERROR("output buffer too small");
module->status = MMAL_EINVAL;
if (mmal_event_error_send(component, module->status) != MMAL_SUCCESS)
LOG_ERROR("unable to send an error event buffer");
goto end;
}
mmal_buffer_header_mem_lock(out);
mmal_buffer_header_mem_lock(in);
memcpy(out->data, in->data + in->offset, in->length);
mmal_buffer_header_mem_unlock(in);
mmal_buffer_header_mem_unlock(out);
out->length = in->length;
out->offset = 0;
out->flags = in->flags;
out->pts = in->pts;
out->dts = in->dts;
*out->type = *in->type;
}
/* Finished with the input buffer, so return it */
in->length = 0;
mmal_port_buffer_header_callback(port_in, in);
in = 0;
if (module->status != MMAL_SUCCESS)
{
LOG_ERROR("failed to replicate buffer");
goto end;
}
/* Request a clock callback when media-time >= pts */
LOG_TRACE("requesting callback at %"PRIi64,out->pts);
port_in->priv->module->last_ts = out->pts;
cb_status = mmal_port_clock_request_add(component->clock[0], out->pts,
scheduler_component_clock_port_request_cb, out);
if (cb_status != MMAL_SUCCESS)
{
LOG_ERROR("failed to add request");
out->length = 0;
mmal_port_buffer_header_callback(port_out, out);
if (cb_status != MMAL_ECORRUPT)
module->status = cb_status;
}
out = 0;
end:
if (in)
mmal_queue_put_back(queue_in, in);
if (out)
mmal_queue_put_back(queue_out, out);
return mmal_queue_length(queue_in) && mmal_queue_length(queue_out);
}
/** Main processing action */
static void scheduler_component_action(MMAL_COMPONENT_T *component)
{
/* Send requests to the clock */
while (scheduler_component_process_buffers(component));
}
/** Destroy a scheduler component */
static MMAL_STATUS_T scheduler_component_destroy(MMAL_COMPONENT_T *component)
{
unsigned int i;
for (i = 0; i < component->input_num; i++)
if (component->input[i]->priv->module->queue)
mmal_queue_destroy(component->input[i]->priv->module->queue);
if (component->input_num)
mmal_ports_free(component->input, component->input_num);
for (i = 0; i < component->output_num; i++)
if (component->output[i]->priv->module->queue)
mmal_queue_destroy(component->output[i]->priv->module->queue);
if (component->output_num)
mmal_ports_free(component->output, component->output_num);
if (component->clock_num)
mmal_ports_clock_free(component->clock, component->clock_num);
vcos_free(component->priv->module);
return MMAL_SUCCESS;
}
/** Enable processing on a port */
static MMAL_STATUS_T scheduler_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
{
MMAL_PARAM_UNUSED(port);
MMAL_PARAM_UNUSED(cb);
return MMAL_SUCCESS;
}
/** Flush a port */
static MMAL_STATUS_T scheduler_port_flush(MMAL_PORT_T *port)
{
MMAL_PORT_MODULE_T *port_module = port->priv->module;
MMAL_BUFFER_HEADER_T *buffer;
/* Flush buffers associated with pending clock requests */
mmal_port_clock_request_flush(port->component->clock[0]);
/* Flush buffers that our component is holding on to */
buffer = mmal_queue_get(port_module->queue);
while (buffer)
{
mmal_port_buffer_header_callback(port, buffer);
buffer = mmal_queue_get(port_module->queue);
}
port->priv->module->last_ts = MMAL_TIME_UNKNOWN;
return MMAL_SUCCESS;
}
/** Disable processing on a port */
static MMAL_STATUS_T scheduler_port_disable(MMAL_PORT_T *port)
{
/* We just need to flush our internal queue */
return scheduler_port_flush(port);
}
/** Send a buffer header to a port */
static MMAL_STATUS_T scheduler_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
MMAL_COMPONENT_T *component = port->component;
/* notify the clock port */
if (port->type == MMAL_PORT_TYPE_INPUT && !buffer->cmd)
{
MMAL_CLOCK_BUFFER_INFO_T info = { buffer->pts, vcos_getmicrosecs() };
mmal_port_clock_input_buffer_info(port->component->clock[0], &info);
}
mmal_queue_put(port->priv->module->queue, buffer);
return mmal_component_action_trigger(component);
}
/** Set format on an input port */
static MMAL_STATUS_T scheduler_input_port_format_commit(MMAL_PORT_T *port)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_EVENT_FORMAT_CHANGED_T *event;
MMAL_PORT_T *output = component->output[0];
MMAL_BUFFER_HEADER_T *buffer;
MMAL_STATUS_T status;
/* If the output port is not enabled we just need to update its format.
* Otherwise we'll have to trigger a format changed event for it. */
if (!output->is_enabled)
{
status = mmal_format_full_copy(output->format, port->format);
return status;
}
/* Send an event on the output port */
status = mmal_port_event_get(output, &buffer, MMAL_EVENT_FORMAT_CHANGED);
if (status != MMAL_SUCCESS)
{
LOG_ERROR("unable to get an event buffer");
return status;
}
event = mmal_event_format_changed_get(buffer);
if (!event)
{
mmal_buffer_header_release(buffer);
LOG_ERROR("failed to set format");
return MMAL_EINVAL;
}
mmal_format_copy(event->format, port->format);
/* Pass on the buffer requirements */
event->buffer_num_min = port->buffer_num_min;
event->buffer_size_min = port->buffer_size_min;
event->buffer_num_recommended = port->buffer_num_recommended;
event->buffer_size_recommended = port->buffer_size_recommended;
mmal_port_event_send(component->output[port->index], buffer);
return status;
}
/** Set format on an output port */
static MMAL_STATUS_T scheduler_output_port_format_commit(MMAL_PORT_T *port)
{
/* The format of the output port needs to match the input port */
if (mmal_format_compare(port->format, port->component->input[port->index]->format))
LOG_DEBUG("output port format different from input port");
return MMAL_SUCCESS;
}
static MMAL_STATUS_T scheduler_port_parameter_set(MMAL_PORT_T *port, const MMAL_PARAMETER_HEADER_T *param)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_PORT_T *in = component->input[port->index], *out = component->input[port->index];
switch (param->id)
{
case MMAL_PARAMETER_BUFFER_REQUIREMENTS:
{
/* Propagate the requirements to the matching input and output the ports */
const MMAL_PARAMETER_BUFFER_REQUIREMENTS_T *req = (const MMAL_PARAMETER_BUFFER_REQUIREMENTS_T *)param;
uint32_t buffer_num_min = MMAL_MAX(port->buffer_num_min, req->buffer_num_min);
uint32_t buffer_num_recommended = MMAL_MAX(port->buffer_num_recommended, req->buffer_num_recommended);
uint32_t buffer_size_min = MMAL_MAX(port->buffer_size_min, req->buffer_size_min);
uint32_t buffer_size_recommended = MMAL_MAX(port->buffer_size_recommended, req->buffer_size_recommended);
in->buffer_num_min = buffer_num_min;
in->buffer_num_recommended = buffer_num_recommended;
in->buffer_size_min = buffer_size_min;
in->buffer_size_recommended = buffer_size_recommended;
out->buffer_num_min = buffer_num_min;
out->buffer_num_recommended = buffer_num_recommended;
out->buffer_size_min = buffer_size_min;
out->buffer_size_recommended = buffer_size_recommended;
}
return MMAL_SUCCESS;
default:
return MMAL_ENOSYS;
}
}
/** Create an instance of a component */
static MMAL_STATUS_T mmal_component_create_scheduler(const char *name, MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module;
MMAL_STATUS_T status = MMAL_ENOMEM;
int disable_passthrough = 0;
unsigned int i;
/* Allocate the context for our module */
component->priv->module = module = vcos_calloc(1, sizeof(*module), "mmal module");
if (!module)
return MMAL_ENOMEM;
component->priv->pf_destroy = scheduler_component_destroy;
/* Allocate and initialise all the ports for this component */
component->input = mmal_ports_alloc(component, SCHEDULER_INPUT_PORTS_NUM,
MMAL_PORT_TYPE_INPUT, sizeof(MMAL_PORT_MODULE_T));
if (!component->input)
goto error;
component->input_num = SCHEDULER_INPUT_PORTS_NUM;
for (i = 0; i < component->input_num; i++)
{
component->input[i]->priv->pf_enable = scheduler_port_enable;
component->input[i]->priv->pf_disable = scheduler_port_disable;
component->input[i]->priv->pf_flush = scheduler_port_flush;
component->input[i]->priv->pf_send = scheduler_port_send;
component->input[i]->priv->pf_set_format = scheduler_input_port_format_commit;
component->input[i]->priv->pf_parameter_set = scheduler_port_parameter_set;
component->input[i]->buffer_num_min = 1;
component->input[i]->buffer_num_recommended = 0;
component->input[i]->capabilities = MMAL_PORT_CAPABILITY_SUPPORTS_EVENT_FORMAT_CHANGE;
component->input[i]->priv->module->queue = mmal_queue_create();
if (!component->input[i]->priv->module->queue)
goto error;
component->input[i]->priv->module->last_ts = MMAL_TIME_UNKNOWN;
}
/* Override passthrough behaviour */
if (strstr(name, ".copy"))
{
LOG_TRACE("disable passthrough on output ports");
disable_passthrough = 1;
}
component->output = mmal_ports_alloc(component, SCHEDULER_OUTPUT_PORTS_NUM,
MMAL_PORT_TYPE_OUTPUT, sizeof(MMAL_PORT_MODULE_T));
if (!component->output)
goto error;
component->output_num = SCHEDULER_OUTPUT_PORTS_NUM;
for (i = 0; i < component->output_num; i++)
{
component->output[i]->priv->pf_enable = scheduler_port_enable;
component->output[i]->priv->pf_disable = scheduler_port_disable;
component->output[i]->priv->pf_flush = scheduler_port_flush;
component->output[i]->priv->pf_send = scheduler_port_send;
component->output[i]->priv->pf_set_format = scheduler_output_port_format_commit;
component->output[i]->priv->pf_parameter_set = scheduler_port_parameter_set;
component->output[i]->buffer_num_min = 1;
component->output[i]->buffer_num_recommended = 0;
component->output[i]->capabilities = disable_passthrough ? 0 : MMAL_PORT_CAPABILITY_PASSTHROUGH;
component->output[i]->priv->module->queue = mmal_queue_create();
if (!component->output[i]->priv->module->queue)
goto error;
}
/* Create the clock port (clock ports are managed by the framework) */
component->clock = mmal_ports_clock_alloc(component, SCHEDULER_CLOCK_PORTS_NUM, 0, NULL);
if (!component->clock)
goto error;
component->clock_num = SCHEDULER_CLOCK_PORTS_NUM;
status = mmal_component_action_register(component, scheduler_component_action);
if (status != MMAL_SUCCESS)
goto error;
return MMAL_SUCCESS;
error:
scheduler_component_destroy(component);
return status;
}
MMAL_CONSTRUCTOR(mmal_register_component_scheduler);
void mmal_register_component_scheduler(void)
{
mmal_component_supplier_register("scheduler", mmal_component_create_scheduler);
}
@@ -0,0 +1,278 @@
/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "mmal.h"
#include "mmal_logging.h"
#include "core/mmal_component_private.h"
#include "core/mmal_port_private.h"
#include <SDL/SDL.h>
#define FRAME_LENGTH 2048
/* Buffering requirements */
#define INPUT_MIN_BUFFER_NUM 4
#define INPUT_RECOMMENDED_BUFFER_NUM 8
/****************************/
typedef struct MMAL_COMPONENT_MODULE_T
{
MMAL_STATUS_T status;
MMAL_QUEUE_T *queue;
MMAL_BOOL_T audio_opened;
} MMAL_COMPONENT_MODULE_T;
/** Destroy a previously created component */
static MMAL_STATUS_T sdl_component_destroy(MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
if (module->audio_opened)
SDL_CloseAudio();
SDL_QuitSubSystem(SDL_INIT_AUDIO);
if(component->input_num) mmal_ports_free(component->input, 1);
if(module->queue) mmal_queue_destroy(module->queue);
vcos_free(module);
return MMAL_SUCCESS;
}
static void sdl_callback( void *ctx, uint8_t *stream, int size )
{
MMAL_PORT_T *port = (MMAL_PORT_T *)ctx;
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_BUFFER_HEADER_T *buffer;
unsigned int i, bytes;
while (size > 0)
{
buffer = mmal_queue_get(module->queue);
if (!buffer)
{
LOG_ERROR("audio underrun");
return;
}
if (port->format->encoding == MMAL_ENCODING_PCM_SIGNED &&
port->format->es->audio.bits_per_sample == 16)
{
bytes = buffer->length;
if (bytes > (unsigned int)size) bytes = size;
memcpy(stream, buffer->data + buffer->offset, bytes);
buffer->offset += bytes;
buffer->length -= bytes;
stream += bytes;
size -= bytes;
}
else if (port->format->es->audio.bits_per_sample == 32)
{
bytes = buffer->length;
if (bytes > 2 * (unsigned int)size) bytes = 2 * size;
vcos_assert(!(bytes&0x3));
if (port->format->encoding == MMAL_ENCODING_PCM_FLOAT)
{
float *in = (float *)(buffer->data + buffer->offset);
int16_t *out = (int16_t *)stream;
for (i = 0; i < bytes / 4; i++)
{
if (*in >= 1.0) *out = 32767;
else if (*in < -1.0) *out = -32768;
else *out = *in * 32768.0;
in++; out++;
}
}
else if (port->format->encoding == MMAL_ENCODING_PCM_SIGNED)
{
int32_t *in = (int32_t *)(buffer->data + buffer->offset);
int16_t *out = (int16_t *)stream;
for (i = 0; i < bytes / 4; i++)
*out++ = (*in++) >> 16;
}
buffer->offset += bytes;
buffer->length -= bytes;
stream += bytes / 2;
size -= bytes / 2;
}
if (buffer->length)
{
/* We still have some data left for next time */
mmal_queue_put_back(module->queue, buffer);
continue;
}
/* Handle the EOS */
if (buffer->flags & MMAL_BUFFER_HEADER_FLAG_EOS)
mmal_event_eos_send(port);
buffer->offset = 0;
mmal_port_buffer_header_callback(port, buffer);
}
}
/** Set format on a port */
static MMAL_STATUS_T sdl_port_set_format(MMAL_PORT_T *port)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
SDL_AudioSpec desired, obtained;
if (port->format->encoding != MMAL_ENCODING_PCM_SIGNED &&
port->format->encoding != MMAL_ENCODING_PCM_FLOAT &&
port->format->es->audio.bits_per_sample != 16 &&
port->format->es->audio.bits_per_sample != 32)
{
LOG_ERROR("port does not support '%4.4s' at %ibps",
(char *)&port->format->encoding, port->format->es->audio.bits_per_sample);
return MMAL_EINVAL;
}
if (module->audio_opened)
SDL_CloseAudio();
module->audio_opened = MMAL_FALSE;
desired.freq = port->format->es->audio.sample_rate;
desired.format = AUDIO_S16SYS;
desired.channels = port->format->es->audio.channels;
desired.callback = sdl_callback;
desired.userdata = port;
desired.samples = FRAME_LENGTH;
/* Open the sound device. */
if (SDL_OpenAudio( &desired, &obtained ) < 0)
return MMAL_ENOSYS;
module->audio_opened = MMAL_TRUE;
/* Now have a look at what we got. */
if (obtained.format != AUDIO_S16SYS)
return MMAL_ENOSYS;
port->format->es->audio.sample_rate = obtained.freq;
port->format->es->audio.channels = obtained.channels;
port->buffer_size_min = obtained.samples * port->format->es->audio.channels * 2;
return MMAL_SUCCESS;
}
/** Enable processing on a port */
static MMAL_STATUS_T sdl_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
{
MMAL_PARAM_UNUSED(port);
MMAL_PARAM_UNUSED(cb);
SDL_PauseAudio( 0 );
return MMAL_SUCCESS;
}
/** Disable processing on a port */
static MMAL_STATUS_T sdl_port_disable(MMAL_PORT_T *port)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_BUFFER_HEADER_T *buffer;
SDL_PauseAudio( 1 );
while((buffer = mmal_queue_get(module->queue)))
mmal_port_buffer_header_callback(port, buffer);
return MMAL_SUCCESS;
}
static MMAL_STATUS_T sdl_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
/* Handle event buffers */
if (buffer->cmd)
{
LOG_ERROR("discarding event %i on port %p", (int)buffer->cmd, port);
buffer->length = 0;
mmal_port_buffer_header_callback(port, buffer);
return MMAL_SUCCESS;
}
if (module->status != MMAL_SUCCESS)
return module->status;
mmal_queue_put(module->queue, buffer);
return MMAL_SUCCESS;
}
/** Create an instance of a component */
static MMAL_STATUS_T mmal_component_create_sdl(const char *name, MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module;
/* Check we're the requested component */
if(strcmp(name, "sdl." MMAL_AUDIO_RENDER))
return MMAL_ENOENT;
if( SDL_WasInit(SDL_INIT_AUDIO) )
return MMAL_ENXIO;
/* Allocate our module context */
component->priv->module = module = vcos_calloc(1, sizeof(*module), "mmal module");
if(!module)
return MMAL_ENOMEM;
if(SDL_Init(SDL_INIT_AUDIO|SDL_INIT_NOPARACHUTE) < 0)
return MMAL_ENXIO;
/* Allocate the ports for this component */
component->input = mmal_ports_alloc(component, 1, MMAL_PORT_TYPE_INPUT, 0);
if(!component->input)
goto error;
component->input_num = 1;
module->queue = mmal_queue_create();
if(!module->queue)
goto error;
component->input[0]->priv->pf_set_format = sdl_port_set_format;
component->input[0]->priv->pf_enable = sdl_port_enable;
component->input[0]->priv->pf_disable = sdl_port_disable;
component->input[0]->priv->pf_send = sdl_port_send;
component->input[0]->buffer_num_min = INPUT_MIN_BUFFER_NUM;
component->input[0]->buffer_num_recommended = INPUT_RECOMMENDED_BUFFER_NUM;
component->priv->pf_destroy = sdl_component_destroy;
return MMAL_SUCCESS;
error:
sdl_component_destroy(component);
return MMAL_ENOMEM;
}
MMAL_CONSTRUCTOR(mmal_register_component_sdl_audio);
void mmal_register_component_sdl_audio(void)
{
mmal_component_supplier_register("sdl", mmal_component_create_sdl);
}
@@ -0,0 +1,394 @@
/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "mmal.h"
#include "mmal_logging.h"
#include "core/mmal_component_private.h"
#include "core/mmal_port_private.h"
#include <SDL/SDL.h>
#define NUM_PORTS_INPUT 1
#define SDL_WIDTH 800
#define SDL_HEIGHT 600
/* Buffering requirements */
#define INPUT_MIN_BUFFER_NUM 1
#define INPUT_RECOMMENDED_BUFFER_NUM 4
/****************************/
typedef struct MMAL_COMPONENT_MODULE_T
{
SDL_Overlay *sdl_overlay;
SDL_Surface *sdl_surface;
unsigned int width;
unsigned int height;
MMAL_STATUS_T status;
MMAL_RECT_T display_region;
MMAL_QUEUE_T *queue;
SDL_Thread *thread;
MMAL_BOOL_T quit;
} MMAL_COMPONENT_MODULE_T;
static MMAL_STATUS_T sdl_port_parameter_set(MMAL_PORT_T *port, const MMAL_PARAMETER_HEADER_T *param)
{
MMAL_STATUS_T status = MMAL_ENOSYS;
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
switch (param->id)
{
case MMAL_PARAMETER_DISPLAYREGION:
{
/* We only support setting the destination rectangle */
const MMAL_DISPLAYREGION_T *display = (const MMAL_DISPLAYREGION_T *)param;
if (display->set & MMAL_DISPLAY_SET_DEST_RECT)
module->display_region = display->dest_rect;
status = MMAL_SUCCESS;
}
break;
default:
break;
}
return status;
}
/** Destroy a previously created component */
static MMAL_STATUS_T sdl_component_destroy(MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
SDL_Event event = {SDL_QUIT};
module->quit = MMAL_TRUE;
SDL_PushEvent(&event);
if(module->thread)
SDL_WaitThread(module->thread, NULL);
if(module->sdl_overlay)
SDL_FreeYUVOverlay(module->sdl_overlay);
if(module->sdl_surface)
SDL_FreeSurface(module->sdl_surface);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
if(component->input_num) mmal_ports_free(component->input, 1);
if(module->queue) mmal_queue_destroy(module->queue);
vcos_free(module);
return MMAL_SUCCESS;
}
static MMAL_STATUS_T mmal_sdl_create_surface(MMAL_COMPONENT_MODULE_T *module)
{
uint32_t flags;
int bpp;
int w = module->display_region.width;
int h = module->display_region.height;
flags = SDL_ANYFORMAT | SDL_HWPALETTE | SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_RESIZABLE;
bpp = SDL_VideoModeOK(w, h, 16, flags);
if(!bpp)
{
LOG_ERROR("no SDL video mode available");
return MMAL_ENOSYS;
}
module->sdl_surface = SDL_SetVideoMode(w, h, bpp, flags);
if(!module->sdl_surface)
{
LOG_ERROR("cannot create SDL surface");
return MMAL_ENOMEM;
}
return MMAL_SUCCESS;
}
/** Set format on a port */
static MMAL_STATUS_T sdl_port_set_format(MMAL_PORT_T *port)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_STATUS_T status;
if ((status=mmal_sdl_create_surface(module)) != MMAL_SUCCESS)
return status;
/* We only support I420 */
if (port->format->encoding != MMAL_ENCODING_I420)
return MMAL_ENOSYS;
/* Check if we need to re-create an overlay */
if (module->sdl_overlay &&
module->width == port->format->es->video.width &&
module->height == port->format->es->video.height)
return MMAL_SUCCESS; /* Nothing to do */
if (module->sdl_overlay)
SDL_FreeYUVOverlay(module->sdl_overlay);
/* Create overlay */
module->sdl_overlay =
SDL_CreateYUVOverlay(port->format->es->video.width,
port->format->es->video.height,
SDL_YV12_OVERLAY, module->sdl_surface);
if (!module->sdl_overlay)
{
LOG_ERROR("cannot create SDL overlay");
return MMAL_ENOSPC;
}
module->width = port->format->es->video.width;
module->height = port->format->es->video.height;
port->buffer_size_min = module->width * module->height * 3 / 2;
return MMAL_SUCCESS;
}
/** Enable processing on a port */
static MMAL_STATUS_T sdl_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
{
MMAL_PARAM_UNUSED(port);
MMAL_PARAM_UNUSED(cb);
return MMAL_SUCCESS;
}
/** Disable processing on a port */
static MMAL_STATUS_T sdl_port_disable(MMAL_PORT_T *port)
{
MMAL_PARAM_UNUSED(port);
return MMAL_SUCCESS;
}
/** Flush a port */
static MMAL_STATUS_T sdl_port_flush(MMAL_PORT_T *port)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_BUFFER_HEADER_T *buffer;
/* Flush buffers that our component is holding on to.
* If the reading thread is holding onto a buffer it will send it back ASAP as well
* so no need to care about that. */
while((buffer = mmal_queue_get(module->queue)))
mmal_port_buffer_header_callback(port, buffer);
return MMAL_SUCCESS;
}
static MMAL_BOOL_T sdl_do_processing(MMAL_COMPONENT_T *component)
{
MMAL_PORT_T *port = component->input[0];
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
unsigned int width = port->format->es->video.width;
unsigned int height = port->format->es->video.height;
MMAL_BUFFER_HEADER_T *buffer;
uint8_t *src_plane[3];
uint32_t *src_pitch;
unsigned int i, line;
MMAL_BOOL_T eos;
SDL_Rect rect;
buffer = mmal_queue_get(module->queue);
if (!buffer)
return 0;
eos = buffer->flags & MMAL_BUFFER_HEADER_FLAG_EOS;
/* Handle event buffers */
if (buffer->cmd)
{
MMAL_EVENT_FORMAT_CHANGED_T *event = mmal_event_format_changed_get(buffer);
if (event)
{
mmal_format_copy(port->format, event->format);
module->status = port->priv->pf_set_format(port);
if (module->status != MMAL_SUCCESS)
{
LOG_ERROR("format not set on port %p", port);
if (mmal_event_error_send(port->component, module->status) != MMAL_SUCCESS)
LOG_ERROR("unable to send an error event buffer");
}
}
else
{
LOG_ERROR("discarding event %i on port %p", (int)buffer->cmd, port);
}
buffer->length = 0;
mmal_port_buffer_header_callback(port, buffer);
return 1;
}
if (module->status != MMAL_SUCCESS)
return 1;
/* Ignore empty buffers */
if (!buffer->length)
goto end;
// FIXME: sanity check the size of the buffer
/* Blit the buffer onto the overlay. */
src_pitch = buffer->type->video.pitch;
src_plane[0] = buffer->data + buffer->type->video.offset[0];
src_plane[1] = buffer->data + buffer->type->video.offset[2];
src_plane[2] = buffer->data + buffer->type->video.offset[1];
SDL_LockYUVOverlay(module->sdl_overlay);
for (i=0; i<3; i++)
{
uint8_t *src = src_plane[i];
uint8_t *dst = module->sdl_overlay->pixels[i];
if(i == 1) {width /= 2; height /= 2;}
for(line = 0; line < height; line++)
{
memcpy(dst, src, width);
src += src_pitch[i];
dst += module->sdl_overlay->pitches[i];
}
}
SDL_UnlockYUVOverlay(module->sdl_overlay);
width = port->format->es->video.width;
height = port->format->es->video.height;
rect.x = module->display_region.x;
rect.w = module->display_region.width;
height = rect.w * height / width;
rect.y = module->display_region.y + (module->display_region.height - height) / 2;
rect.h = height;
SDL_DisplayYUVOverlay(module->sdl_overlay, &rect);
end:
buffer->offset = buffer->length = 0;
mmal_port_buffer_header_callback(port, buffer);
/* Generate EOS events */
if (eos)
mmal_event_eos_send(port);
return 1;
}
/*****************************************************************************/
static void sdl_do_processing_loop(MMAL_COMPONENT_T *component)
{
while (sdl_do_processing(component));
}
/** Buffer sending */
static MMAL_STATUS_T sdl_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
mmal_queue_put(module->queue, buffer);
mmal_component_action_trigger(port->component);
return MMAL_SUCCESS;
}
/** SDL event thread */
static int sdl_event_thread(void *arg)
{
MMAL_COMPONENT_T *component = (MMAL_COMPONENT_T *)arg;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
SDL_Event event;
while (SDL_WaitEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
if (!module->quit)
mmal_event_error_send(component, MMAL_SUCCESS);
return 0;
default:
break;
}
}
return 0;
}
/** Create an instance of a component */
static MMAL_STATUS_T mmal_component_create_sdl(const char *name, MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module;
MMAL_STATUS_T status = MMAL_ENOMEM;
/* Check we're the requested component */
if(strcmp(name, "sdl." MMAL_VIDEO_RENDER))
return MMAL_ENOENT;
if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_EVENTTHREAD|SDL_INIT_NOPARACHUTE) < 0)
return MMAL_ENXIO;
/* Allocate our module context */
component->priv->module = module = vcos_calloc(1, sizeof(*module), "mmal module");
if(!module) return MMAL_ENOMEM;
module->queue = mmal_queue_create();
if(!module->queue) goto error;
/* Allocate the ports for this component */
component->input = mmal_ports_alloc(component, 1, MMAL_PORT_TYPE_INPUT, 0);
if(!component->input) goto error;
component->input_num = 1;
module->display_region.width = SDL_WIDTH;
module->display_region.height = SDL_HEIGHT;
/************/
component->input[0]->priv->pf_set_format = sdl_port_set_format;
component->input[0]->priv->pf_enable = sdl_port_enable;
component->input[0]->priv->pf_disable = sdl_port_disable;
component->input[0]->priv->pf_flush = sdl_port_flush;
component->input[0]->priv->pf_send = sdl_port_send;
component->input[0]->priv->pf_parameter_set = sdl_port_parameter_set;
component->input[0]->buffer_num_min = INPUT_MIN_BUFFER_NUM;
component->input[0]->buffer_num_recommended = INPUT_RECOMMENDED_BUFFER_NUM;
component->priv->pf_destroy = sdl_component_destroy;
/* Create a thread to monitor SDL events */
module->thread = SDL_CreateThread(sdl_event_thread, component);
status = mmal_component_action_register(component, sdl_do_processing_loop);
if (status != MMAL_SUCCESS)
goto error;
return MMAL_SUCCESS;
error:
sdl_component_destroy(component);
return status;
}
MMAL_CONSTRUCTOR(mmal_register_component_sdl);
void mmal_register_component_sdl(void)
{
mmal_component_supplier_register("sdl", mmal_component_create_sdl);
}
@@ -0,0 +1,496 @@
/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "mmal.h"
#include "core/mmal_component_private.h"
#include "core/mmal_port_private.h"
#include "mmal_logging.h"
#define SPDIF_AC3_FRAME_SIZE 6144
#define SPDIF_EAC3_FRAME_SIZE (6144*4)
#define SPDIF_FRAME_SIZE SPDIF_EAC3_FRAME_SIZE
/* Buffering requirements */
#define INPUT_MIN_BUFFER_SIZE SPDIF_FRAME_SIZE
#define INPUT_MIN_BUFFER_NUM 2
#define OUTPUT_MIN_BUFFER_SIZE SPDIF_FRAME_SIZE
#define OUTPUT_MIN_BUFFER_NUM 2
/*****************************************************************************/
typedef struct MMAL_COMPONENT_MODULE_T
{
MMAL_STATUS_T status; /**< current status of the component */
} MMAL_COMPONENT_MODULE_T;
typedef struct MMAL_PORT_MODULE_T
{
MMAL_QUEUE_T *queue; /**< queue for the buffers sent to the ports */
MMAL_BOOL_T needs_configuring; /**< port is waiting for a format commit */
} MMAL_PORT_MODULE_T;
/*****************************************************************************/
/*****************************************************************************/
static MMAL_STATUS_T spdif_send_event_format_changed(MMAL_COMPONENT_T *component, MMAL_PORT_T *port,
MMAL_ES_FORMAT_T *format)
{
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_BUFFER_HEADER_T *buffer = NULL;
MMAL_EVENT_FORMAT_CHANGED_T *event;
/* Get an event buffer */
module->status = mmal_port_event_get(port, &buffer, MMAL_EVENT_FORMAT_CHANGED);
if (module->status != MMAL_SUCCESS)
{
LOG_ERROR("unable to get an event buffer");
return module->status;
}
/* coverity[returned_null] Can't return null or call above would have failed */
event = mmal_event_format_changed_get(buffer);
/* Fill in the new format */
if (port->format->encoding == MMAL_ENCODING_PCM_SIGNED)
mmal_format_copy(event->format, port->format);
else
mmal_format_copy(event->format, format);
event->format->es->audio.sample_rate = format->es->audio.sample_rate;
/* Pass on the buffer requirements */
event->buffer_num_min = port->buffer_num_min;
event->buffer_size_min = port->buffer_size_min;
event->buffer_size_recommended = event->buffer_size_min;
event->buffer_num_recommended = port->buffer_num_recommended;
port->priv->module->needs_configuring = 1;
mmal_port_event_send(port, buffer);
return MMAL_SUCCESS;
}
/** Actual processing function */
static MMAL_BOOL_T spdif_do_processing(MMAL_COMPONENT_T *component)
{
static const uint8_t ac3_spdif_header[6] = {0x72,0xF8,0x1F,0x4E,0x1, 0};
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_PORT_T *port_in = component->input[0];
MMAL_PORT_T *port_out = component->output[0];
MMAL_BUFFER_HEADER_T *in, *out;
unsigned int i, sample_rate, frame_size, spdif_frame_size;
uint8_t *in_data;
if (port_out->priv->module->needs_configuring)
return 0;
in = mmal_queue_get(port_in->priv->module->queue);
if (!in)
return 0;
/* Handle event buffers */
if (in->cmd)
{
MMAL_EVENT_FORMAT_CHANGED_T *event = mmal_event_format_changed_get(in);
if (event)
{
module->status = mmal_format_full_copy(port_in->format, event->format);
if (module->status == MMAL_SUCCESS)
module->status = port_in->priv->pf_set_format(port_in);
if (module->status != MMAL_SUCCESS)
{
LOG_ERROR("format not set on port %s %p (%i)", port_in->name, port_in, module->status);
if (mmal_event_error_send(component, module->status) != MMAL_SUCCESS)
LOG_ERROR("unable to send an error event buffer");
}
}
else
{
LOG_ERROR("discarding event %i on port %s %p", (int)in->cmd, port_in->name, port_in);
}
in->length = 0;
mmal_port_buffer_header_callback(port_in, in);
return 1;
}
/* Don't do anything if we've already seen an error */
if (module->status != MMAL_SUCCESS)
{
mmal_queue_put_back(port_in->priv->module->queue, in);
return 0;
}
/* Discard empty buffers */
if (!in->length && !in->flags)
{
mmal_port_buffer_header_callback(port_in, in);
return 1;
}
/* Discard codec config data as it's not needed */
if (in->flags & MMAL_BUFFER_HEADER_FLAG_CONFIG)
{
LOG_DEBUG("config buffer %ibytes", in->length);
in->length = 0;
mmal_port_buffer_header_callback(port_in, in);
return 1;
}
out = mmal_queue_get(port_out->priv->module->queue);
if (!out)
{
mmal_queue_put_back(port_in->priv->module->queue, in);
return 0;
}
spdif_frame_size = SPDIF_AC3_FRAME_SIZE;
if (port_out->format->encoding == MMAL_ENCODING_EAC3)
spdif_frame_size = SPDIF_EAC3_FRAME_SIZE;
/* Sanity check the output buffer is big enough */
if (out->alloc_size < spdif_frame_size)
{
module->status = MMAL_EINVAL;
if (mmal_event_error_send(component, module->status) != MMAL_SUCCESS)
LOG_ERROR("unable to send an error event buffer");
mmal_queue_put_back(port_in->priv->module->queue, in);
mmal_queue_put_back(port_out->priv->module->queue, out);
return 0;
}
/* Special case for empty buffers carrying a flag */
if (!in->length && in->flags)
{
out->length = 0;
goto end;
}
LOG_DEBUG("frame: %lld, size %i", in->pts, in->length);
mmal_buffer_header_mem_lock(out);
mmal_buffer_header_mem_lock(in);
in_data = in->data + in->offset;
/* Sanity check we're dealing with an AC3 frame */
if (in->length < 5)
{
LOG_ERROR("invalid data size (%i bytes)", in->length);
goto discard;
}
if (!(in_data[0] == 0x0B || in_data[1] == 0x77) &&
!(in_data[0] == 0x77 || in_data[1] == 0x0B))
{
LOG_ERROR("invalid data (%i bytes): %2.2x,%2.2x,%2.2x,%2.2x",
in->length, in_data[0], in_data[1], in_data[2], in_data[3]);
goto discard;
}
/* We need to make sure we use the right sample rate
* to be able to play this at the right rate */
if ((in_data[4] & 0xC0) == 0x40) sample_rate = 44100;
else if ((in_data[4] & 0xC0) == 0x80) sample_rate = 32000;
else sample_rate = 48000;
/* If the sample rate changes, stop everything we're doing
* and signal the format change. */
if (sample_rate != port_out->format->es->audio.sample_rate)
{
LOG_INFO("format change: %i->%i",
port_out->format->es->audio.sample_rate, sample_rate);
port_in->format->es->audio.sample_rate = sample_rate;
spdif_send_event_format_changed(component, port_out, port_in->format);
mmal_buffer_header_mem_unlock(in);
mmal_buffer_header_mem_unlock(out);
mmal_queue_put_back(port_in->priv->module->queue, in);
mmal_queue_put_back(port_out->priv->module->queue, out);
return 0;
}
/* Build our S/PDIF frame. We assume that we need to send
* little endian S/PDIF data. */
if (in->length > spdif_frame_size - 8)
LOG_ERROR("frame too big, truncating (%i/%i bytes)",
in->length, spdif_frame_size - 8);
frame_size = MMAL_MIN(in->length, spdif_frame_size - 8) / 2;
memcpy(out->data, ac3_spdif_header, sizeof(ac3_spdif_header));
out->data[5] = in_data[5] & 0x7; /* bsmod */
out->data[6] = frame_size & 0xFF;
out->data[7] = frame_size >> 8;
/* Copy the AC3 data, reverting the endianness if required */
if (in_data[0] == 0x0B)
{
for (i = 0; i < frame_size; i++)
{
out->data[8+i*2] = in_data[in->offset+i*2+1];
out->data[8+i*2+1] = in_data[in->offset+i*2];
}
}
else
memcpy(out->data + 8, in_data, in->length);
/* The S/PDIF frame needs to be padded */
memset(out->data + 8 + frame_size * 2, 0,
spdif_frame_size - frame_size * 2 - 8);
mmal_buffer_header_mem_unlock(in);
mmal_buffer_header_mem_unlock(out);
out->length = spdif_frame_size;
end:
out->offset = 0;
out->flags = in->flags;
out->pts = in->pts;
out->dts = in->dts;
/* Send buffers back */
in->length = 0;
mmal_port_buffer_header_callback(port_in, in);
mmal_port_buffer_header_callback(port_out, out);
return 1;
discard:
mmal_buffer_header_mem_unlock(in);
mmal_buffer_header_mem_unlock(out);
in->length = 0;
mmal_queue_put_back(port_out->priv->module->queue, out);
mmal_port_buffer_header_callback(port_in, in);
return 1;
}
/*****************************************************************************/
static void spdif_do_processing_loop(MMAL_COMPONENT_T *component)
{
while (spdif_do_processing(component));
}
/** Destroy a previously created component */
static MMAL_STATUS_T spdif_component_destroy(MMAL_COMPONENT_T *component)
{
unsigned int i;
for(i = 0; i < component->input_num; i++)
if(component->input[i]->priv->module->queue)
mmal_queue_destroy(component->input[i]->priv->module->queue);
if(component->input_num)
mmal_ports_free(component->input, component->input_num);
for(i = 0; i < component->output_num; i++)
if(component->output[i]->priv->module->queue)
mmal_queue_destroy(component->output[i]->priv->module->queue);
if(component->output_num)
mmal_ports_free(component->output, component->output_num);
vcos_free(component->priv->module);
return MMAL_SUCCESS;
}
/** Enable processing on a port */
static MMAL_STATUS_T spdif_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
{
MMAL_PARAM_UNUSED(cb);
/* We need to propagate the buffer requirements when the input port is
* enabled */
if (port->type == MMAL_PORT_TYPE_INPUT)
return port->priv->pf_set_format(port);
return MMAL_SUCCESS;
}
/** Flush a port */
static MMAL_STATUS_T spdif_port_flush(MMAL_PORT_T *port)
{
MMAL_PORT_MODULE_T *port_module = port->priv->module;
MMAL_BUFFER_HEADER_T *buffer;
/* Flush buffers that our component is holding on to */
buffer = mmal_queue_get(port_module->queue);
while(buffer)
{
mmal_port_buffer_header_callback(port, buffer);
buffer = mmal_queue_get(port_module->queue);
}
return MMAL_SUCCESS;
}
/** Disable processing on a port */
static MMAL_STATUS_T spdif_port_disable(MMAL_PORT_T *port)
{
/* We just need to flush our internal queue */
return spdif_port_flush(port);
}
/** Send a buffer header to a port */
static MMAL_STATUS_T spdif_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
mmal_queue_put(port->priv->module->queue, buffer);
mmal_component_action_trigger(port->component);
return MMAL_SUCCESS;
}
/** Set format on input port */
static MMAL_STATUS_T spdif_input_port_format_commit(MMAL_PORT_T *in)
{
MMAL_COMPONENT_T *component = in->component;
MMAL_PORT_T *out = component->output[0];
/* Sanity check we cope with this format */
if (in->format->encoding != MMAL_ENCODING_AC3 &&
in->format->encoding != MMAL_ENCODING_EAC3)
return MMAL_ENXIO;
LOG_INFO("%4.4s, %iHz, %ichan, %ibps", (char *)&in->format->encoding,
in->format->es->audio.sample_rate, in->format->es->audio.channels,
in->format->bitrate);
/* TODO: should we check the bitrate to see if that fits in an S/PDIF
* frame? */
/* Check if there's anything to propagate to the output port */
if (!mmal_format_compare(in->format, out->format))
return MMAL_SUCCESS;
if (out->format->encoding == MMAL_ENCODING_PCM_SIGNED &&
in->format->es->audio.sample_rate ==
out->format->es->audio.sample_rate)
return MMAL_SUCCESS;
/* If the output port is not enabled we just need to update its format.
* Otherwise we'll have to trigger a format changed event for it. */
if (!out->is_enabled)
{
if (out->format->encoding != MMAL_ENCODING_PCM_SIGNED)
mmal_format_copy(out->format, in->format);
out->format->es->audio.sample_rate = in->format->es->audio.sample_rate;
return MMAL_SUCCESS;
}
/* Send an event on the output port */
return spdif_send_event_format_changed(component, out, in->format);
}
/** Set format on output port */
static MMAL_STATUS_T spdif_output_port_format_commit(MMAL_PORT_T *out)
{
int supported = 0;
if (out->format->type == MMAL_ES_TYPE_AUDIO &&
out->format->encoding == MMAL_ENCODING_PCM_SIGNED &&
out->format->es->audio.channels == 2 &&
out->format->es->audio.bits_per_sample == 16)
supported = 1;
if (out->format->type == MMAL_ES_TYPE_AUDIO &&
(out->format->encoding == MMAL_ENCODING_AC3 ||
out->format->encoding == MMAL_ENCODING_EAC3))
supported = 1;
if (!supported)
{
LOG_ERROR("invalid format %4.4s, %ichan, %ibps",
(char *)&out->format->encoding, out->format->es->audio.channels,
out->format->es->audio.bits_per_sample);
return MMAL_EINVAL;
}
out->priv->module->needs_configuring = 0;
mmal_component_action_trigger(out->component);
return MMAL_SUCCESS;
}
/** Create an instance of a component */
static MMAL_STATUS_T mmal_component_create_spdif(const char *name, MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module;
MMAL_STATUS_T status = MMAL_ENOMEM;
MMAL_PARAM_UNUSED(name);
/* Allocate the context for our module */
component->priv->module = module = vcos_malloc(sizeof(*module), "mmal module");
if (!module)
return MMAL_ENOMEM;
memset(module, 0, sizeof(*module));
component->priv->pf_destroy = spdif_component_destroy;
/* Allocate and initialise all the ports for this component */
component->input = mmal_ports_alloc(component, 1, MMAL_PORT_TYPE_INPUT, sizeof(MMAL_PORT_MODULE_T));
if(!component->input)
goto error;
component->input_num = 1;
component->input[0]->priv->pf_enable = spdif_port_enable;
component->input[0]->priv->pf_disable = spdif_port_disable;
component->input[0]->priv->pf_flush = spdif_port_flush;
component->input[0]->priv->pf_send = spdif_port_send;
component->input[0]->priv->pf_set_format = spdif_input_port_format_commit;
component->input[0]->priv->module->queue = mmal_queue_create();
if(!component->input[0]->priv->module->queue)
goto error;
component->output = mmal_ports_alloc(component, 1, MMAL_PORT_TYPE_OUTPUT, sizeof(MMAL_PORT_MODULE_T));
if(!component->output)
goto error;
component->output_num = 1;
component->output[0]->priv->pf_enable = spdif_port_enable;
component->output[0]->priv->pf_disable = spdif_port_disable;
component->output[0]->priv->pf_flush = spdif_port_flush;
component->output[0]->priv->pf_send = spdif_port_send;
component->output[0]->priv->pf_set_format = spdif_output_port_format_commit;
component->output[0]->priv->module->queue = mmal_queue_create();
if(!component->output[0]->priv->module->queue)
goto error;
status = mmal_component_action_register(component, spdif_do_processing_loop);
if (status != MMAL_SUCCESS)
goto error;
component->input[0]->format->type = MMAL_ES_TYPE_AUDIO;
component->input[0]->format->encoding = MMAL_ENCODING_AC3;
component->input[0]->buffer_size_min =
component->input[0]->buffer_size_recommended = INPUT_MIN_BUFFER_SIZE;
component->input[0]->buffer_num_min =
component->input[0]->buffer_num_recommended = INPUT_MIN_BUFFER_NUM;
component->output[0]->format->type = MMAL_ES_TYPE_AUDIO;
component->output[0]->format->encoding = MMAL_ENCODING_AC3;
component->output[0]->format->es->audio.channels = 2;
component->output[0]->format->es->audio.bits_per_sample = 16;
component->output[0]->buffer_size_min =
component->output[0]->buffer_size_recommended = OUTPUT_MIN_BUFFER_SIZE;
component->output[0]->buffer_num_min =
component->output[0]->buffer_num_recommended = OUTPUT_MIN_BUFFER_NUM;
return MMAL_SUCCESS;
error:
spdif_component_destroy(component);
return status;
}
MMAL_CONSTRUCTOR(mmal_register_component_spdif);
void mmal_register_component_spdif(void)
{
mmal_component_supplier_register("spdif", mmal_component_create_spdif);
}
@@ -0,0 +1,345 @@
/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "mmal.h"
#include "core/mmal_component_private.h"
#include "core/mmal_port_private.h"
#include "mmal_logging.h"
#define SPLITTER_OUTPUT_PORTS_NUM 4 /* 4 should do for now */
/*****************************************************************************/
typedef struct MMAL_COMPONENT_MODULE_T
{
uint32_t enabled_flags; /**< Flags indicating which output port is enabled */
uint32_t sent_flags; /**< Flags indicating which output port we've already sent data to */
MMAL_BOOL_T error; /**< Error state */
} MMAL_COMPONENT_MODULE_T;
typedef struct MMAL_PORT_MODULE_T
{
MMAL_QUEUE_T *queue; /**< queue for the buffers sent to the ports */
} MMAL_PORT_MODULE_T;
/*****************************************************************************/
/** Destroy a previously created component */
static MMAL_STATUS_T splitter_component_destroy(MMAL_COMPONENT_T *component)
{
unsigned int i;
for(i = 0; i < component->input_num; i++)
if(component->input[i]->priv->module->queue)
mmal_queue_destroy(component->input[i]->priv->module->queue);
if(component->input_num)
mmal_ports_free(component->input, component->input_num);
for(i = 0; i < component->output_num; i++)
if(component->output[i]->priv->module->queue)
mmal_queue_destroy(component->output[i]->priv->module->queue);
if(component->output_num)
mmal_ports_free(component->output, component->output_num);
vcos_free(component->priv->module);
return MMAL_SUCCESS;
}
/** Enable processing on a port */
static MMAL_STATUS_T splitter_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
{
#if 0
MMAL_COMPONENT_T *component = port->component;
uint32_t buffer_num, buffer_size;
unsigned int i;
/* Find the max and apply that to all ports */
buffer_num = component->input[0]->buffer_num;
buffer_size = component->input[0]->buffer_size;
for (i = 0; i < component->output_num; i++)
{
buffer_num = MMAL_MAX(buffer_num, component->output[i]->buffer_num);
buffer_size = MMAL_MAX(buffer_num, component->output[i]->buffer_size);
}
component->input[0]->buffer_num = buffer_num;
component->input[0]->buffer_size = buffer_size;
for (i = 0; i < component->output_num; i++)
{
component->output[i]->buffer_num = buffer_num;
component->output[i]->buffer_size = buffer_num;
}
#endif
MMAL_PARAM_UNUSED(cb);
if (port->buffer_size)
if (port->type == MMAL_PORT_TYPE_OUTPUT)
port->component->priv->module->enabled_flags |= (1<<port->index);
return MMAL_SUCCESS;
}
/** Flush a port */
static MMAL_STATUS_T splitter_port_flush(MMAL_PORT_T *port)
{
MMAL_PORT_MODULE_T *port_module = port->priv->module;
MMAL_BUFFER_HEADER_T *buffer;
/* Flush buffers that our component is holding on to */
buffer = mmal_queue_get(port_module->queue);
while(buffer)
{
mmal_port_buffer_header_callback(port, buffer);
buffer = mmal_queue_get(port_module->queue);
}
if (port->type == MMAL_PORT_TYPE_INPUT)
port->component->priv->module->sent_flags = 0;
return MMAL_SUCCESS;
}
/** Disable processing on a port */
static MMAL_STATUS_T splitter_port_disable(MMAL_PORT_T *port)
{
if (port->type == MMAL_PORT_TYPE_OUTPUT)
port->component->priv->module->enabled_flags &= ~(1<<port->index);
/* We just need to flush our internal queue */
return splitter_port_flush(port);
}
/** Send a buffer header to a port */
static MMAL_STATUS_T splitter_send_output(MMAL_BUFFER_HEADER_T *buffer, MMAL_PORT_T *out_port)
{
MMAL_BUFFER_HEADER_T *out;
MMAL_STATUS_T status;
/* Get a buffer header from output port */
out = mmal_queue_get(out_port->priv->module->queue);
if (!out)
return MMAL_EAGAIN;
/* Copy our input buffer header */
status = mmal_buffer_header_replicate(out, buffer);
if (status != MMAL_SUCCESS)
goto error;
/* Send buffer back */
mmal_port_buffer_header_callback(out_port, out);
return MMAL_SUCCESS;
error:
mmal_queue_put_back(out_port->priv->module->queue, out);
return status;
}
/** Send a buffer header to a port */
static MMAL_STATUS_T splitter_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_COMPONENT_MODULE_T *module = component->priv->module;
MMAL_PORT_T *in_port, *out_port;
MMAL_BUFFER_HEADER_T *in;
MMAL_STATUS_T status;
unsigned int i;
mmal_queue_put(port->priv->module->queue, buffer);
if (module->error)
return MMAL_SUCCESS; /* Just do nothing */
/* Get input buffer header */
in_port = component->input[0];
in = mmal_queue_get(in_port->priv->module->queue);
if (!in)
return MMAL_SUCCESS; /* Nothing to do */
for (i = 0; i < component->output_num; i++)
{
out_port = component->output[i];
status = splitter_send_output(in, out_port);
if (status != MMAL_SUCCESS && status != MMAL_EAGAIN)
goto error;
if (status == MMAL_SUCCESS)
module->sent_flags |= (1<<i);
}
/* Check if we're done with the input buffer */
if ((module->sent_flags & module->enabled_flags) == module->enabled_flags)
{
in->length = 0; /* Consume the input buffer */
mmal_port_buffer_header_callback(in_port, in);
module->sent_flags = 0;
return MMAL_SUCCESS;
}
/* We're not done yet so put the buffer back in the queue */
mmal_queue_put(in_port->priv->module->queue, in);
return MMAL_SUCCESS;
error:
mmal_queue_put(in_port->priv->module->queue, in);
status = mmal_event_error_send(port->component, status);
if (status != MMAL_SUCCESS)
{
LOG_ERROR("unable to send an error event buffer (%i)", (int)status);
return MMAL_SUCCESS;
}
module->error = 1;
return MMAL_SUCCESS;
}
/** Set format on a port */
static MMAL_STATUS_T splitter_port_format_commit(MMAL_PORT_T *port)
{
MMAL_COMPONENT_T *component = port->component;
MMAL_STATUS_T status;
unsigned int i;
/* Sanity check */
if (port->type == MMAL_PORT_TYPE_OUTPUT)
{
LOG_ERROR("output port is read-only");
return MMAL_EINVAL;
}
/* Commit the format on all output ports */
for (i = 0; i < component->output_num; i++)
{
status = mmal_format_full_copy(component->output[i]->format, port->format);
if (status != MMAL_SUCCESS)
return status;
}
return MMAL_SUCCESS;
}
static MMAL_STATUS_T splitter_port_parameter_set(MMAL_PORT_T *port, const MMAL_PARAMETER_HEADER_T *param)
{
MMAL_COMPONENT_T *component = port->component;
unsigned int i;
switch (param->id)
{
case MMAL_PARAMETER_BUFFER_REQUIREMENTS:
{
/* Propagate the requirements to all the ports */
const MMAL_PARAMETER_BUFFER_REQUIREMENTS_T *req = (const MMAL_PARAMETER_BUFFER_REQUIREMENTS_T *)param;
uint32_t buffer_num_min = MMAL_MAX(port->buffer_num_min, req->buffer_num_min);
uint32_t buffer_num_recommended = MMAL_MAX(port->buffer_num_recommended, req->buffer_num_recommended);
uint32_t buffer_size_min = MMAL_MAX(port->buffer_size_min, req->buffer_size_min);
uint32_t buffer_size_recommended = MMAL_MAX(port->buffer_size_recommended, req->buffer_size_recommended);
component->input[0]->buffer_num_min = buffer_num_min;
component->input[0]->buffer_num_recommended = buffer_num_recommended;
component->input[0]->buffer_size_min = buffer_size_min;
component->input[0]->buffer_size_recommended = buffer_size_recommended;
for (i = 0; i < component->output_num; i++)
{
component->output[i]->buffer_num_min = buffer_num_min;
component->output[i]->buffer_num_recommended = buffer_num_recommended;
component->output[i]->buffer_size_min = buffer_size_min;
component->output[i]->buffer_size_recommended = buffer_size_recommended;
}
}
return MMAL_SUCCESS;
default:
return MMAL_ENOSYS;
}
}
/** Create an instance of a component */
static MMAL_STATUS_T mmal_component_create_splitter(const char *name, MMAL_COMPONENT_T *component)
{
MMAL_COMPONENT_MODULE_T *module;
MMAL_STATUS_T status = MMAL_ENOMEM;
unsigned int i;
MMAL_PARAM_UNUSED(name);
/* Allocate the context for our module */
component->priv->module = module = vcos_malloc(sizeof(*module), "mmal module");
if (!module)
return MMAL_ENOMEM;
memset(module, 0, sizeof(*module));
component->priv->pf_destroy = splitter_component_destroy;
/* Allocate and initialise all the ports for this component */
component->input = mmal_ports_alloc(component, 1, MMAL_PORT_TYPE_INPUT, sizeof(MMAL_PORT_MODULE_T));
if(!component->input)
goto error;
component->input_num = 1;
component->input[0]->priv->pf_enable = splitter_port_enable;
component->input[0]->priv->pf_disable = splitter_port_disable;
component->input[0]->priv->pf_flush = splitter_port_flush;
component->input[0]->priv->pf_send = splitter_port_send;
component->input[0]->priv->pf_set_format = splitter_port_format_commit;
component->input[0]->priv->pf_parameter_set = splitter_port_parameter_set;
component->input[0]->buffer_num_min = 1;
component->input[0]->buffer_num_recommended = 0;
component->input[0]->priv->module->queue = mmal_queue_create();
if(!component->input[0]->priv->module->queue)
goto error;
component->output = mmal_ports_alloc(component, SPLITTER_OUTPUT_PORTS_NUM,
MMAL_PORT_TYPE_OUTPUT, sizeof(MMAL_PORT_MODULE_T));
if(!component->output)
goto error;
component->output_num = SPLITTER_OUTPUT_PORTS_NUM;
for(i = 0; i < component->output_num; i++)
{
component->output[i]->priv->pf_enable = splitter_port_enable;
component->output[i]->priv->pf_disable = splitter_port_disable;
component->output[i]->priv->pf_flush = splitter_port_flush;
component->output[i]->priv->pf_send = splitter_port_send;
component->output[i]->priv->pf_set_format = splitter_port_format_commit;
component->output[i]->priv->pf_parameter_set = splitter_port_parameter_set;
component->output[i]->buffer_num_min = 1;
component->output[i]->buffer_num_recommended = 0;
component->output[i]->capabilities = MMAL_PORT_CAPABILITY_PASSTHROUGH;
component->output[i]->priv->module->queue = mmal_queue_create();
if(!component->output[i]->priv->module->queue)
goto error;
}
return MMAL_SUCCESS;
error:
splitter_component_destroy(component);
return status;
}
MMAL_CONSTRUCTOR(mmal_register_component_splitter);
void mmal_register_component_splitter(void)
{
mmal_component_supplier_register("splitter", mmal_component_create_splitter);
}