Änderungen Gruppe 30_10_2022 V2
This commit is contained in:
@@ -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)
|
||||
|
||||
+504
@@ -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);
|
||||
}
|
||||
+861
@@ -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);
|
||||
}
|
||||
+287
@@ -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);
|
||||
}
|
||||
+607
@@ -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);
|
||||
}
|
||||
+586
@@ -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);
|
||||
}
|
||||
+1000
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 = ∈
|
||||
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);
|
||||
}
|
||||
|
||||
+278
@@ -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);
|
||||
}
|
||||
+394
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user