I'll post the mmal routines here. Maybe you can look it over to see if there are any obvious errors.
OpenMMalDecoder. Why does it need 20 input buffers?
Code: Select all
int FfmpegCamera::OpenMmalDecoder(AVCodecContext *mVideoCodecContext){
// Create the decoder component.
if ( mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &decoder) != MMAL_SUCCESS) {
Fatal("failed to create mmal decoder");
}
// CONTROL PORT SETTINGS
decoder->control->userdata = (MMAL_PORT_USERDATA_T *)&context;
if ( mmal_port_enable(decoder->control, control_callback) != MMAL_SUCCESS ) {
Fatal("failed to enable mmal decoder control port");
}
/* Get statistics on the input port */
MMAL_PARAMETER_CORE_STATISTICS_T stats = {{0}};
stats.hdr.id = MMAL_PARAMETER_CORE_STATISTICS;
stats.hdr.size = sizeof(MMAL_PARAMETER_CORE_STATISTICS_T);
if (mmal_port_parameter_get(decoder->input[0], &stats.hdr) != MMAL_SUCCESS) {
Info("failed to get decoder port statistics");
}
else {
Info("Decoder stats: %i, %i", stats.stats.buffer_count, stats.stats.max_delay);
}
/* Set the zero-copy parameter on the input port */
MMAL_PARAMETER_BOOLEAN_T zc = {{MMAL_PARAMETER_ZERO_COPY, sizeof(zc)}, MMAL_TRUE};
if (mmal_port_parameter_set(decoder->input[0], &zc.hdr) != MMAL_SUCCESS)
Info("Failed to set zero copy on decoder input");
/* Set the zero-copy parameter on the output port */
if (mmal_port_parameter_set_boolean(decoder->output[0], MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE) != MMAL_SUCCESS)
Info("Failed to set zero copy on decoder output");
/* Set format of video decoder input port */
MMAL_ES_FORMAT_T *format_in = decoder->input[0]->format;
format_in->type = MMAL_ES_TYPE_VIDEO;
format_in->encoding = MMAL_ENCODING_H264;
format_in->es->video.width = VCOS_ALIGN_UP(width, 32);
format_in->es->video.height = VCOS_ALIGN_UP(height,16);
format_in->es->video.crop.width = mVideoCodecContext->width;
format_in->es->video.crop.height = mVideoCodecContext->height;
format_in->es->video.frame_rate.num = 24000;
format_in->es->video.frame_rate.den = 1001;
format_in->es->video.par.num = mVideoCodecContext->sample_aspect_ratio.num;
format_in->es->video.par.den = mVideoCodecContext->sample_aspect_ratio.den;
format_in->flags = MMAL_ES_FORMAT_FLAG_FRAMED;
if ( mmal_port_format_commit(decoder->input[0]) != MMAL_SUCCESS ) {
Fatal("failed to commit mmal decoder input format");
}
MMAL_ES_FORMAT_T *format_out = decoder->output[0]->format;
format_out->type = MMAL_ES_TYPE_VIDEO;
format_out->encoding = MMAL_ENCODING_I420;
//ALLOCATE Extradata, copying from avcodec context
if (mmal_format_extradata_alloc(format_in, mVideoCodecContext->extradata_size) != MMAL_SUCCESS)
Fatal("failed to allocate extradata ");
Info("Decoder extradata size %d\n", mVideoCodecContext->extradata_size);
format_in->extradata_size = mVideoCodecContext->extradata_size;
if (format_in->extradata_size)
memcpy(format_in->extradata, mVideoCodecContext->extradata, mVideoCodecContext->extradata_size);
if ( mmal_port_format_commit(decoder->output[0]) != MMAL_SUCCESS ) {
Fatal("failed to commit decoder output format");
}
/* The format of both ports is now set so we can get their buffer requirements and create
* our buffer headers. We use the buffer pool API to create these. */
decoder->input[0]->buffer_num = decoder->input[0]->buffer_num_recommended;
decoder->input[0]->buffer_size = decoder->input[0]->buffer_size_recommended;
decoder->output[0]->buffer_num = decoder->output[0]->buffer_num_recommended;
decoder->output[0]->buffer_size = decoder->output[0]->buffer_size_recommended;
pool_ind = mmal_port_pool_create(decoder->input[0],decoder->input[0]->buffer_num,
decoder->input[0]->buffer_size);
pool_outd = mmal_port_pool_create(decoder->output[0],decoder->output[0]->buffer_num,
decoder->output[0]->buffer_size);
/* Display the input port format */
display_format(&decoder->input[0],&format_in);
display_format(&decoder->output[0],&format_out);
/* Create a queue to store our decoded video frames. The callback we will get when
* a frame has been decoded will put the frame into this queue. */
context.dqueue = mmal_queue_create();
/* Store a reference to our context in each port (will be used during callbacks) */
decoder->input[0]->userdata = (MMAL_PORT_USERDATA_T *)&context;
decoder->output[0]->userdata = (MMAL_PORT_USERDATA_T *)&context;
// Enable all the input port and the output port.
if ( mmal_port_enable(decoder->input[0], input_callback) != MMAL_SUCCESS ) {
Fatal("failed to enable mmal decoder input port");
}
if ( mmal_port_enable(decoder->output[0], output_callbackd) != MMAL_SUCCESS ) {
Fatal("failed to enable mmal decoder output port");
}
/* Component won't start processing data until it is enabled. */
if ( mmal_component_enable(decoder) != MMAL_SUCCESS ) {
Fatal("failed to enable mmal decoder component");
}
return 0;
}
OpenMMalEncoder. Only used for getting motion vectors.
Code: Select all
int FfmpegCamera::OpenMmalEncoder(AVCodecContext *mVideoCodecContext){
// Create the encoder component.
if ( mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, &encoder) != MMAL_SUCCESS) {
Fatal("failed to create mmal encoder");
}
// CONTROL PORT SETTINGS
encoder->control->userdata = (MMAL_PORT_USERDATA_T *)&context;
if ( mmal_port_enable(encoder->control, control_callback) != MMAL_SUCCESS ) {
Fatal("failed to enable mmal encoder control port");
}
/* Get statistics on the input port */
MMAL_PARAMETER_CORE_STATISTICS_T stats = {{0}};
stats.hdr.id = MMAL_PARAMETER_CORE_STATISTICS;
stats.hdr.size = sizeof(MMAL_PARAMETER_CORE_STATISTICS_T);
if (mmal_port_parameter_get(encoder->input[0], &stats.hdr) != MMAL_SUCCESS) {
Info("failed to get encoder port statistics");
}
else {
Info("Encoder stats: %i, %i", stats.stats.buffer_count, stats.stats.max_delay);
}
/* Set the zero-copy parameter on the input port */
MMAL_PARAMETER_BOOLEAN_T zc = {{MMAL_PARAMETER_ZERO_COPY, sizeof(zc)}, MMAL_TRUE};
if (mmal_port_parameter_set(encoder->input[0], &zc.hdr) != MMAL_SUCCESS)
Info("Failed to set zero copy on encoder input");
/* Set the zero-copy parameter on the output port */
if (mmal_port_parameter_set_boolean(encoder->output[0], MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE) != MMAL_SUCCESS)
Info("Failed to set zero copy on encoder output");
/* Set format of video encoder input port */
MMAL_ES_FORMAT_T *format_in = encoder->input[0]->format;
format_in->type = MMAL_ES_TYPE_VIDEO;
format_in->encoding = MMAL_ENCODING_I420;
format_in->es->video.width = VCOS_ALIGN_UP(width, 32);
format_in->es->video.height = VCOS_ALIGN_UP(height,16);
format_in->es->video.crop.width = mVideoCodecContext->width;
format_in->es->video.crop.height = mVideoCodecContext->height;
format_in->es->video.frame_rate.num = 24000;
format_in->es->video.frame_rate.den = 1001;
format_in->es->video.par.num = mVideoCodecContext->sample_aspect_ratio.num;
format_in->es->video.par.den = mVideoCodecContext->sample_aspect_ratio.den;
format_in->flags = MMAL_ES_FORMAT_FLAG_FRAMED;
if ( mmal_port_format_commit(encoder->input[0]) != MMAL_SUCCESS ) {
Fatal("failed to commit mmal encoder input format");
}
MMAL_ES_FORMAT_T *format_out = encoder->output[0]->format;
format_out->type = MMAL_ES_TYPE_VIDEO;
format_out->encoding = MMAL_ENCODING_H264;
format_out->es->video.width = VCOS_ALIGN_UP(width, 32);
format_out->es->video.height = VCOS_ALIGN_UP(height,16);
format_out->es->video.crop.width = mVideoCodecContext->width;
format_out->es->video.crop.height = mVideoCodecContext->height;
if ( mmal_port_format_commit(encoder->output[0]) != MMAL_SUCCESS ) {
Fatal("failed to commit encoder output format");
}
if (mmal_port_parameter_set_boolean(encoder->output[0], MMAL_PARAMETER_VIDEO_ENCODE_INLINE_VECTORS, 1) != MMAL_SUCCESS) {
Fatal("failed to request inline motion vectors from mmal encoder");
}
/* Display the input port format */
display_format(&encoder->input[0],&format_in);
display_format(&encoder->output[0],&format_out);
/* The format of both ports is now set so we can get their buffer requirements and create
* our buffer headers. We use the buffer pool API to create these. */
encoder->input[0]->buffer_num = encoder->input[0]->buffer_num_min;
encoder->input[0]->buffer_size = encoder->input[0]->buffer_size_min;
encoder->output[0]->buffer_num = encoder->output[0]->buffer_num_min;
encoder->output[0]->buffer_size = encoder->output[0]->buffer_size_min;
pool_ine = mmal_port_pool_create(encoder->input[0],encoder->input[0]->buffer_num,
encoder->input[0]->buffer_size);
pool_oute = mmal_port_pool_create(encoder->output[0],encoder->output[0]->buffer_num,
encoder->output[0]->buffer_size);
/* Create a queue to store our decoded video frames. The callback we will get when
* a frame has been decoded will put the frame into this queue. */
context.equeue = mmal_queue_create();
/* Store a reference to our context in each port (will be used during callbacks) */
encoder->input[0]->userdata = (MMAL_PORT_USERDATA_T *)&context;
encoder->output[0]->userdata = (MMAL_PORT_USERDATA_T *)&context;
// Enable all the input port and the output port.
if ( mmal_port_enable(encoder->input[0], input_callback) != MMAL_SUCCESS ) {
Fatal("failed to enable mmal encoder input port");
}
if ( mmal_port_enable(encoder->output[0], output_callbacke) != MMAL_SUCCESS ) {
Fatal("failed to enable mmal encoder output port");
}
/* Component won't start processing data until it is enabled. */
if ( mmal_component_enable(encoder) != MMAL_SUCCESS ) {
Fatal("failed to enable mmal encoder component");
}
return 0;
}
OpenMMalResizer. This is only being used for format conversion right now. This creates the RGB buffers which are sent to the analyse process and eventually converted to JPEG there. I think the software jpeg conversion is the slowest part in the analyse process and probably why the analyze process lags the capture process.
Code: Select all
int FfmpegCamera::OpenMmalResizer(AVCodecContext *mVideoCodecContext){
// Create the Resizer component.
if ( mmal_component_create("vc.ril.isp", &resizer) != MMAL_SUCCESS) {
Fatal("failed to create mmal resizer");
}
// CONTROL PORT SETTINGS
resizer->control->userdata = (MMAL_PORT_USERDATA_T *)&context;
if ( mmal_port_enable(resizer->control, control_callback) != MMAL_SUCCESS ) {
Fatal("failed to enable mmal resizer control port");
}
/* Get statistics on the input port */
MMAL_PARAMETER_CORE_STATISTICS_T stats = {{0}};
stats.hdr.id = MMAL_PARAMETER_CORE_STATISTICS;
stats.hdr.size = sizeof(MMAL_PARAMETER_CORE_STATISTICS_T);
if (mmal_port_parameter_get(resizer->input[0], &stats.hdr) != MMAL_SUCCESS) {
Info("failed to get resizer port statistics");
}
else {
Info("Resizer stats: %i, %i", stats.stats.buffer_count, stats.stats.max_delay);
}
/* Set the zero-copy parameter on the input port */
MMAL_PARAMETER_BOOLEAN_T zc = {{MMAL_PARAMETER_ZERO_COPY, sizeof(zc)}, MMAL_TRUE};
if (mmal_port_parameter_set(resizer->input[0], &zc.hdr) != MMAL_SUCCESS)
Info("Failed to set zero copy on resizer input");
/* Set the zero-copy parameter on the output port */
if (mmal_port_parameter_set_boolean(resizer->output[0], MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE) != MMAL_SUCCESS)
Info("Failed to set zero copy on resizer output");
/* Set format of video resizer input port */
MMAL_ES_FORMAT_T *format_in = resizer->input[0]->format;
format_in->type = MMAL_ES_TYPE_VIDEO;
format_in->encoding = MMAL_ENCODING_I420;
format_in->encoding_variant = MMAL_ENCODING_I420;
format_in->es->video.width = VCOS_ALIGN_UP(width, 32);
format_in->es->video.height = VCOS_ALIGN_UP(height,16);
format_in->es->video.crop.width = mVideoCodecContext->width;
format_in->es->video.crop.height = mVideoCodecContext->height;
format_in->es->video.frame_rate.num = 24000;
format_in->es->video.frame_rate.den = 1001;
format_in->es->video.par.num = mVideoCodecContext->sample_aspect_ratio.num;
format_in->es->video.par.den = mVideoCodecContext->sample_aspect_ratio.den;
format_in->flags = MMAL_ES_FORMAT_FLAG_FRAMED;
if ( mmal_port_format_commit(resizer->input[0]) != MMAL_SUCCESS ) {
Fatal("failed to commit mmal resizer input format");
}
MMAL_ES_FORMAT_T *format_out = resizer->output[0]->format;
format_out->es->video.width = VCOS_ALIGN_UP(width, 32);
format_out->es->video.height = VCOS_ALIGN_UP(height,16);
format_out->es->video.crop.width = mVideoCodecContext->width;
format_out->es->video.crop.height = mVideoCodecContext->height;
if ( colours == ZM_COLOUR_RGB32 ) {
format_out->encoding = MMAL_ENCODING_RGBA;
} else if ( colours == ZM_COLOUR_RGB24 ) {
format_out->encoding = MMAL_ENCODING_RGB24;
} else if(colours == ZM_COLOUR_GRAY8) {
format_out->encoding = MMAL_ENCODING_I420;
}
if ( mmal_port_format_commit(resizer->output[0]) != MMAL_SUCCESS ) {
Fatal("failed to commit mmal resizer output format");
}
/* Display the input port format */
display_format(&resizer->input[0],&format_in);
display_format(&resizer->output[0],&format_out);
/* The format of both ports is now set so we can get their buffer requirements and create
* our buffer headers. We use the buffer pool API to create these. */
resizer->input[0]->buffer_num = resizer->input[0]->buffer_num_min;
resizer->input[0]->buffer_size = resizer->input[0]->buffer_size_min;
resizer->output[0]->buffer_num = resizer->output[0]->buffer_num_min;
resizer->output[0]->buffer_size = resizer->output[0]->buffer_size_min;
pool_inr = mmal_port_pool_create(resizer->input[0],resizer->input[0]->buffer_num,
resizer->input[0]->buffer_size);
pool_outr = mmal_port_pool_create(resizer->output[0],resizer->output[0]->buffer_num,
resizer->output[0]->buffer_size);
/* Create a queue to store our decoded video frames. The callback we will get when
* a frame has been decoded will put the frame into this queue. */
context.rqueue = mmal_queue_create();
/* Store a reference to our context in each port (will be used during callbacks) */
resizer->input[0]->userdata = (MMAL_PORT_USERDATA_T *)&context;
resizer->output[0]->userdata = (MMAL_PORT_USERDATA_T *)&context;
// Enable all the input port and the output port.
if ( mmal_port_enable(resizer->input[0], input_callback) != MMAL_SUCCESS ) {
Fatal("failed to enable mmal resizer input port");
}
if ( mmal_port_enable(resizer->output[0], output_callbackr) != MMAL_SUCCESS ) {
Fatal("failed to enable mmal resizer output port");
}
/* Component won't start processing data until it is enabled. */
if ( mmal_component_enable(resizer) != MMAL_SUCCESS ) {
Fatal("failed to enable mmal resizer component");
}
return 0;
}
Decode Routine creates an AVFrame.
Code: Select all
...
frameComplete=mmal_decode(&packet);
...
Encoder Routine takes the AVFrame payload and puts it in the input buffer. Motion vectors are saved in a buffer and sent to analyse process via shared mem.
Code: Select all
...
mmal_encode(&mvect_buffer);
...
Resizer Routine takes the AVFrame payload and puts it in the input buffer. The RGB data is saved in a buffer and sent to analyse process via shared mem.
mmal_decode.
Code: Select all
int FfmpegCamera::mmal_decode(AVPacket *pkt) {
MMAL_BUFFER_HEADER_T *buffer;
int got_frame=false;
//Info("decode start");
if ((buffer = mmal_queue_get(pool_ind->queue)) != NULL) {
memcpy(buffer->data,pkt->data,pkt->size);
buffer->length=pkt->size;
buffer->flags|=MMAL_BUFFER_HEADER_FLAG_FRAME_START;
buffer->flags|=MMAL_BUFFER_HEADER_FLAG_FRAME_END;
buffer->pts = pkt->pts == AV_NOPTS_VALUE ? MMAL_TIME_UNKNOWN : pkt->pts;
buffer->dts = pkt->dts == AV_NOPTS_VALUE ? MMAL_TIME_UNKNOWN : pkt->dts;
buffer->alloc_size = decoder->input[0]->buffer_size;
if (mmal_port_send_buffer(decoder->input[0], buffer) != MMAL_SUCCESS) {
Warning("failed to send H264 buffer to decoder for frame %d\n", frameCount);
}
}
while ((buffer = mmal_queue_get(context.dqueue)) != NULL)
{
//save it as AVFrame holding an I420 buffer
av_image_fill_arrays(mRawFrame->data, mRawFrame->linesize, buffer->data, AV_PIX_FMT_YUV420P, mRawFrame->width, mRawFrame->height, 1);
got_frame=true;
mmal_buffer_header_release(buffer);
}
//if ((buffer = mmal_queue_get(pool_outd->queue)) != NULL) {
while ((buffer = mmal_queue_get(pool_outd->queue)) != NULL){
if (mmal_port_send_buffer(decoder->output[0], buffer) != MMAL_SUCCESS) {
Warning("failed to send empty buffer to decoder output for frame %d\n", frameCount);
}
}
//Info("decode end");
return (got_frame);
}
mmal_encode.
Code: Select all
int FfmpegCamera::mmal_encode(uint8_t **mv_buffer) { //uses mRawFrame data
MMAL_BUFFER_HEADER_T *buffer;
//Info("encode start");
uint16_t numblocks=((encoder->output[0]->format->es->video.width * encoder->output[0]->format->es->video.height)/256);
if ((buffer = mmal_queue_get(pool_ine->queue)) != NULL) {
av_image_copy_to_buffer(buffer->data, bufsize, (const uint8_t **)mRawFrame->data, mRawFrame->linesize,
AV_PIX_FMT_YUV420P, mRawFrame->width, mRawFrame->height, 1);
buffer->length=bufsize;
buffer->pts = buffer->dts = MMAL_TIME_UNKNOWN;
buffer->alloc_size = encoder->input[0]->buffer_size;
if (mmal_port_send_buffer(encoder->input[0], buffer) != MMAL_SUCCESS) {
Warning("failed to send I420 buffer to encoder for frame %d\n", frameCount);
}
}
while ((buffer = mmal_queue_get(context.equeue)) != NULL) {
if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_CODECSIDEINFO) {
uint16_t t_offset=0;
uint32_t registers;
mmal_motion_vector *mvarray=(mmal_motion_vector *)buffer->data;
registers=0;
uint16_t count=0;
uint16_t wcount=0;
for (int i=0;i < numblocks ; i++) {
if ((abs(mvarray[i].x_vector) + abs(mvarray[i].y_vector)) > 8)
registers =registers | (1 << count);
count++;
if ( count == 32) {
memcpy((*mv_buffer)+t_offset , ®isters, 4 ) ;
count=0;
wcount+=1;
t_offset+=4;
registers=0;
}
}
}
mmal_buffer_header_release(buffer);
}
//if ((buffer = mmal_queue_get(pool_out->queue)) != NULL) {
while ((buffer = mmal_queue_get(pool_oute->queue)) != NULL) {
if (mmal_port_send_buffer(encoder->output[0], buffer) != MMAL_SUCCESS) {
Warning("failed to send buffer to encoder output for frame %d\n", frameCount);
}
}
//Info("encode end");
return (0);
}
mmal_resize.
Code: Select all
int FfmpegCamera::mmal_resize(uint8_t** dbuffer) { //uses mRawFrame data
MMAL_BUFFER_HEADER_T *buffer;
//Info("resize start");
if ((buffer = mmal_queue_get(pool_inr->queue)) != NULL) {
av_image_copy_to_buffer(buffer->data, bufsize, (const uint8_t **)mRawFrame->data, mRawFrame->linesize,
AV_PIX_FMT_YUV420P, mRawFrame->width, mRawFrame->height, 1);
buffer->length=bufsize;
buffer->pts = buffer->dts = MMAL_TIME_UNKNOWN;
//buffer->flags=packet->flags;
buffer->alloc_size = resizer->input[0]->buffer_size;
if (mmal_port_send_buffer(resizer->input[0], buffer) != MMAL_SUCCESS) {
Warning("failed to send I420 buffer to resizer for frame %d\n", frameCount);
}
}
while ((buffer = mmal_queue_get(context.rqueue)) != NULL){
memcpy((*dbuffer),buffer->data,width*height*colours);
mmal_buffer_header_release(buffer);
}
//if ((buffer = mmal_queue_get(pool_outr->queue)) != NULL) {
while ((buffer = mmal_queue_get(pool_outr->queue)) != NULL) {
if (mmal_port_send_buffer(resizer->output[0], buffer) != MMAL_SUCCESS) {
Warning("failed to send buffer to resizer output for frame %d\n", frameCount);
}
}
//Info("resize end");
return (0);
}
Mmal unload routine.
Code: Select all
int FfmpegCamera::CloseMmal(){
mmal_port_disable(decoder->input[0]);
mmal_port_disable(decoder->output[0]);
mmal_port_disable(decoder->control);
mmal_port_flush(decoder->input[0]);
mmal_port_flush(decoder->output[0]);
mmal_port_flush(decoder->control);
mmal_port_disable(encoder->input[0]);
mmal_port_disable(encoder->output[0]);
mmal_port_disable(encoder->control);
mmal_port_flush(encoder->input[0]);
mmal_port_flush(encoder->output[0]);
mmal_port_flush(encoder->control);
mmal_port_disable(resizer->input[0]);
mmal_port_disable(resizer->output[0]);
mmal_port_disable(resizer->control);
mmal_port_flush(resizer->input[0]);
mmal_port_flush(resizer->output[0]);
mmal_port_flush(resizer->control);
if (decoder)
mmal_component_destroy(decoder);
if (resizer)
mmal_component_destroy(resizer);
if (encoder)
mmal_component_destroy(encoder);
if (pool_ind)
mmal_pool_destroy(pool_ind);
if (pool_outd)
mmal_pool_destroy(pool_outd);
if (pool_inr)
mmal_pool_destroy(pool_inr);
if (pool_outr)
mmal_pool_destroy(pool_outr);
if (pool_ine)
mmal_pool_destroy(pool_ine);
if (pool_oute)
mmal_pool_destroy(pool_oute);
if (context.equeue)
mmal_queue_destroy(context.equeue);
if (context.rqueue)
mmal_queue_destroy(context.rqueue);
if (context.dqueue)
mmal_queue_destroy(context.dqueue);
return 0;
}
Here is a link to the source code.
https://github.com/cmisip/ZoneMinder/bl ... camera.cpp
Thanks,
Chris