rpicamsrc: First version which generates buffers on the src pad

Fixed to 1920x1080 h264 regardless of caps.
This commit is contained in:
Jan Schmidt 2013-10-12 19:23:03 +11:00 committed by Tim-Philipp Müller
parent 7ffb618b20
commit 1416631df9
3 changed files with 129 additions and 49 deletions

View file

@ -57,6 +57,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <memory.h> #include <memory.h>
#include <sysexits.h> #include <sysexits.h>
#include <gst/gst.h>
#include "bcm_host.h" #include "bcm_host.h"
#include "interface/vcos/vcos.h" #include "interface/vcos/vcos.h"
@ -124,6 +125,8 @@ struct RASPIVID_STATE_T
MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port
PORT_USERDATA callback_data; PORT_USERDATA callback_data;
MMAL_QUEUE_T *encoded_buffer_q;
}; };
#if 0 #if 0
@ -499,57 +502,62 @@ static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf
*/ */
static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{ {
MMAL_BUFFER_HEADER_T *new_buffer;
// We pass our file handle and other stuff in via the userdata field.
PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata;
RASPIVID_STATE *state = pData->state; RASPIVID_STATE *state = pData->state;
if (pData) if (pData == NULL)
{
int bytes_written = buffer->length;
vcos_assert(state->output_file);
if (buffer->length)
{
mmal_buffer_header_mem_lock(buffer);
bytes_written = fwrite(buffer->data, 1, buffer->length, state->output_file);
mmal_buffer_header_mem_unlock(buffer);
}
if (bytes_written != buffer->length)
{
vcos_log_error("Failed to write buffer data (%d from %d)- aborting", bytes_written, buffer->length);
pData->abort = 1;
}
}
else
{ {
vcos_log_error("Received a encoder buffer callback with no state"); vcos_log_error("Received a encoder buffer callback with no state");
// release buffer back to the pool
mmal_buffer_header_release(buffer);
return;
} }
/* Send buffer to GStreamer element for pushing to the pipeline */
mmal_queue_put(state->encoded_buffer_q, buffer);
}
GstFlowReturn
raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **bufp)
{
GstBuffer *buf;
MMAL_BUFFER_HEADER_T *buffer;
GstFlowReturn ret = GST_FLOW_ERROR;
/* FIXME: Use our own interruptible cond wait: */
buffer = mmal_queue_wait(state->encoded_buffer_q);
mmal_buffer_header_mem_lock(buffer);
buf = gst_buffer_new_allocate(NULL, buffer->length, NULL);
if (buf) {
gst_buffer_fill(buf, 0, buffer->data, buffer->length);
g_print("Buffer of size %u\n", (guint) buffer->length);
ret = GST_FLOW_OK;
}
mmal_buffer_header_mem_unlock(buffer);
*bufp = buf;
// release buffer back to the pool // release buffer back to the pool
mmal_buffer_header_release(buffer); mmal_buffer_header_release(buffer);
// and send one back to the port (if still open) // and send one back to the port (if still open)
if (port->is_enabled) if (state->encoder_output_port->is_enabled)
{ {
MMAL_STATUS_T status = MMAL_SUCCESS; MMAL_STATUS_T status = MMAL_SUCCESS;
new_buffer = mmal_queue_get(state->encoder_pool->queue); buffer = mmal_queue_get(state->encoder_pool->queue);
if (buffer)
status = mmal_port_send_buffer(state->encoder_output_port, buffer);
if (new_buffer) if (!buffer || status != MMAL_SUCCESS) {
status = mmal_port_send_buffer(port, new_buffer);
if (!new_buffer || status != MMAL_SUCCESS)
vcos_log_error("Unable to return a buffer to the encoder port"); vcos_log_error("Unable to return a buffer to the encoder port");
ret = GST_FLOW_ERROR;
}
} }
}
return ret;
}
/** /**
* Create the camera component, set up its ports * Create the camera component, set up its ports
@ -785,6 +793,8 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state)
if (encoder_output->buffer_size < encoder_output->buffer_size_min) if (encoder_output->buffer_size < encoder_output->buffer_size_min)
encoder_output->buffer_size = encoder_output->buffer_size_min; encoder_output->buffer_size = encoder_output->buffer_size_min;
g_print("encoder buffer size is %u\n", (guint)encoder_output->buffer_size);
encoder_output->buffer_num = encoder_output->buffer_num_recommended; encoder_output->buffer_num = encoder_output->buffer_num_recommended;
if (encoder_output->buffer_num < encoder_output->buffer_num_min) if (encoder_output->buffer_num < encoder_output->buffer_num_min)
@ -859,7 +869,6 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state)
/* Create pool of buffer headers for the output port to consume */ /* Create pool of buffer headers for the output port to consume */
pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size); pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size);
if (!pool) if (!pool)
{ {
vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name); vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name);
@ -888,6 +897,11 @@ static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state)
*/ */
static void destroy_encoder_component(RASPIVID_STATE *state) static void destroy_encoder_component(RASPIVID_STATE *state)
{ {
/* Empty the buffer header q */
while (mmal_queue_length(state->encoded_buffer_q))
(void)(mmal_queue_get(state->encoded_buffer_q));
mmal_queue_destroy(state->encoded_buffer_q);
// Get rid of any port buffers first // Get rid of any port buffers first
if (state->encoder_pool) if (state->encoder_pool)
{ {
@ -1004,6 +1018,8 @@ raspi_capture_start(RASPIVID_CONFIG *config)
state->camera_still_port = state->camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; state->camera_still_port = state->camera_component->output[MMAL_CAMERA_CAPTURE_PORT];
state->encoder_output_port = state->encoder_component->output[0]; state->encoder_output_port = state->encoder_component->output[0];
state->encoded_buffer_q = mmal_queue_create();
if (state->config.preview_parameters.wantPreview ) if (state->config.preview_parameters.wantPreview )
{ {
if (state->config.verbose) if (state->config.verbose)
@ -1096,7 +1112,7 @@ raspi_capture_start(RASPIVID_CONFIG *config)
// Only encode stuff if we have a filename and it opened // Only encode stuff if we have a filename and it opened
if (state->output_file) if (state->output_file)
{ {
int wait; //int wait;
if (state->config.verbose) if (state->config.verbose)
fprintf(stderr, "Starting video capture\n"); fprintf(stderr, "Starting video capture\n");
@ -1127,6 +1143,7 @@ raspi_capture_start(RASPIVID_CONFIG *config)
// out of storage space) // out of storage space)
// Going to check every ABORT_INTERVAL milliseconds // Going to check every ABORT_INTERVAL milliseconds
#if 0
for (wait = 0; state->config.timeout == 0 || wait < state->config.timeout; wait+= ABORT_INTERVAL) for (wait = 0; state->config.timeout == 0 || wait < state->config.timeout; wait+= ABORT_INTERVAL)
{ {
vcos_sleep(ABORT_INTERVAL); vcos_sleep(ABORT_INTERVAL);
@ -1136,13 +1153,16 @@ raspi_capture_start(RASPIVID_CONFIG *config)
if (state->config.verbose) if (state->config.verbose)
fprintf(stderr, "Finished capture\n"); fprintf(stderr, "Finished capture\n");
#endif
} }
else else
{ {
#if 0
if (state->config.timeout) if (state->config.timeout)
vcos_sleep(state->config.timeout); vcos_sleep(state->config.timeout);
else else
for (;;) vcos_sleep(ABORT_INTERVAL); for (;;) vcos_sleep(ABORT_INTERVAL);
#endif
} }
} }

View file

@ -83,6 +83,7 @@ typedef struct RASPIVID_STATE_T RASPIVID_STATE;
void raspicapture_init(); void raspicapture_init();
void raspicapture_default_config(RASPIVID_CONFIG *config); void raspicapture_default_config(RASPIVID_CONFIG *config);
RASPIVID_STATE *raspi_capture_start(RASPIVID_CONFIG *config); RASPIVID_STATE *raspi_capture_start(RASPIVID_CONFIG *config);
GstFlowReturn raspi_capture_fill_buffer(RASPIVID_STATE *state, GstBuffer **buf);
void raspi_capture_stop(RASPIVID_STATE *state); void raspi_capture_stop(RASPIVID_STATE *state);
G_END_DECLS G_END_DECLS

View file

@ -121,7 +121,11 @@ static void gst_rpi_cam_src_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec); GValue * value, GParamSpec * pspec);
static gboolean gst_rpi_cam_src_start (GstBaseSrc *parent); static gboolean gst_rpi_cam_src_start (GstBaseSrc *parent);
static gboolean gst_rpi_cam_src_stop (GstBaseSrc *parent); static gboolean gst_rpi_cam_src_stop (GstBaseSrc *parent);
static GstFlowReturn gst_rpi_cam_src_fill_buffer (GstPushSrc *parent, GstBuffer *buf); static gboolean gst_rpi_cam_src_decide_allocation (GstBaseSrc * src,
GstQuery * query);
static GstFlowReturn gst_rpi_cam_src_create (GstPushSrc *parent, GstBuffer **buf);
static GstCaps *gst_rpi_cam_src_get_caps (GstBaseSrc * src, GstCaps * filter);
static GstCaps *gst_rpi_cam_src_fixate (GstBaseSrc * basesrc, GstCaps * caps);
static void static void
gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass) gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass)
@ -150,7 +154,10 @@ gst_rpi_cam_src_class_init (GstRpiCamSrcClass * klass)
basesrc_class->start = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_start); basesrc_class->start = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_start);
basesrc_class->stop = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_stop); basesrc_class->stop = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_stop);
pushsrc_class->fill = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_fill_buffer); basesrc_class->decide_allocation = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_decide_allocation);
basesrc_class->get_caps = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_get_caps);
basesrc_class->fixate = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_fixate);
pushsrc_class->create = GST_DEBUG_FUNCPTR(gst_rpi_cam_src_create);
raspicapture_init(); raspicapture_init();
} }
@ -197,6 +204,7 @@ static gboolean
gst_rpi_cam_src_start (GstBaseSrc *parent) gst_rpi_cam_src_start (GstBaseSrc *parent)
{ {
GstRpiCamSrc *src = GST_RPICAMSRC(parent); GstRpiCamSrc *src = GST_RPICAMSRC(parent);
g_print ("In src_start()\n");
src->capture_state = raspi_capture_start(&src->capture_config); src->capture_state = raspi_capture_start(&src->capture_config);
return TRUE; return TRUE;
} }
@ -209,10 +217,61 @@ gst_rpi_cam_src_stop (GstBaseSrc *parent)
return TRUE; return TRUE;
} }
static GstFlowReturn static GstCaps *
gst_rpi_cam_src_fill_buffer (GstPushSrc *parent, GstBuffer *buf) gst_rpi_cam_src_get_caps (GstBaseSrc * src, GstCaps * filter)
{ {
return GST_FLOW_ERROR; g_print ("In get_caps\n");
//if (src->state == NULL)
return gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (src));
/* Retrieve limiting parameters from the camera module, max width/height fps-range */
}
static gboolean gst_rpi_cam_src_decide_allocation (GstBaseSrc *bsrc,
GstQuery * query)
{
g_print ("In decide_allocation\n");
return GST_BASE_SRC_CLASS (parent_class)->decide_allocation (bsrc, query);
}
static GstCaps *gst_rpi_cam_src_fixate (GstBaseSrc * basesrc, GstCaps * caps)
{
GstStructure *structure;
gint i;
GST_DEBUG_OBJECT (basesrc, "fixating caps %" GST_PTR_FORMAT, caps);
caps = gst_caps_make_writable (caps);
for (i = 0; i < gst_caps_get_size (caps); ++i) {
structure = gst_caps_get_structure (caps, i);
/* Fixate to the camera's 1920x1080 resolution if possible */
gst_structure_fixate_field_nearest_int (structure, "width", 1920);
gst_structure_fixate_field_nearest_int (structure, "height", 1080);
gst_structure_fixate_field_nearest_fraction (structure, "framerate",
G_MAXINT, 1);
gst_structure_fixate_field (structure, "format");
}
GST_DEBUG_OBJECT (basesrc, "fixated caps %" GST_PTR_FORMAT, caps);
caps = GST_BASE_SRC_CLASS (parent_class)->fixate (basesrc, caps);
return caps;
}
static GstFlowReturn
gst_rpi_cam_src_create (GstPushSrc *parent, GstBuffer **buf)
{
GstRpiCamSrc *src = GST_RPICAMSRC(parent);
GstFlowReturn ret;
/* FIXME: Use custom allocator */
ret = raspi_capture_fill_buffer(src->capture_state, buf);
if (*buf)
g_print ("Made buffer of size %" G_GSIZE_FORMAT "\n", gst_buffer_get_size(*buf));
return ret;
} }
static gboolean static gboolean