I am actually using imagemagick display to display the jpeg file. The test program I put together saves the jpeg output to a file. Connecting the decoder output to jpeg encoder directly results in a green band on the bottom. If I connect vc.ril.isp between the decoder and jpeg encoder to do an intermediate conversion to RGBA, the green band is replaced by a black band. This is not as distracting as a green band. The source video is an rtsp stream at 640x360 from an SV3C camera. Here is the test code I use, just in case you may spot errors. I don't know why I get a run of bad jpegs (gray pixels only) at the start. Its ok after about 120 frames.
Code: Select all
#include "interface/mmal/client/brcmjpeg/brcmjpeg.h"
#include "bcm_host.h"
#include "interface/mmal/mmal.h"
#include "interface/mmal/util/mmal_default_components.h"
#include "interface/mmal/util/mmal_util_params.h"
#include "interface/vcos/vcos.h"
#include "interface/mmal/vc/mmal_vc_api.h"
#include "interface/mmal/util/mmal_connection.h"
#include <stdio.h>
#include <fcntl.h>
#include <mutex>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/motion_vector.h>
#include <libavutil/imgutils.h>
#include <libavformat/avformat.h>
#include "libswscale/swscale.h"
#include <libavutil/frame.h>
#include "libavformat/avformat.h"
#include <libswscale/swscale.h>
}
//FUNCTIONS
#define CHECK_STATUS(status, msg) if (status != MMAL_SUCCESS) { fprintf(stderr, msg"\n"); }
struct CONTEXT_T {
MMAL_QUEUE_T *queue=NULL;
MMAL_STATUS_T status;
} context;
MMAL_STATUS_T status = MMAL_EINVAL;
MMAL_COMPONENT_T *decoder, *rgbcoder = NULL;
MMAL_POOL_T *pool_in = NULL, *pool_out = NULL;
MMAL_PORT_T *input_port=NULL;
MMAL_PORT_T *output_port=NULL;
MMAL_CONNECTION_T *connection;
BRCMJPEG_T *jcoder;
BRCMJPEG_STATUS_T jstatus;
BRCMJPEG_REQUEST_T jpeg_request;
//hold the jpeg buffer and size
struct JPEG_T {
//uint32_t data_size=0;
long unsigned int data_size=0;
uint32_t buffer_length=0;
uint8_t *data=NULL;
JPEG_T(uint32_t isize){
data=(uint8_t*)av_mallocz(isize);
buffer_length=isize;
}
~JPEG_T(){
if (data)
av_free(data);
}
};
AVDictionary *opts = NULL;
int ret;
AVStream *st;
const char *src_filename = NULL;
AVFormatContext *fmt_ctx = NULL;
AVCodecContext *video_dec_ctx = NULL;
AVStream *video_stream = NULL;
AVCodec *dec = NULL;
AVCodecContext *dec_ctx = NULL;
int video_stream_idx = -1;
AVFrame *frame = NULL;
AVPacket pkt;
MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection)
{
MMAL_STATUS_T status;
status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_OUTPUT);
if (status == MMAL_SUCCESS)
{
status = mmal_connection_enable(*connection);
if (status != MMAL_SUCCESS)
mmal_connection_destroy(*connection);
}
return status;
}
/** Callback from the control port.
* Component is sending us an event. */
void control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
struct CONTEXT_T *ctx = (struct CONTEXT_T *)port->userdata;
switch (buffer->cmd)
{
case MMAL_EVENT_EOS:
/* Only sink component generate EOS events */
fprintf(stderr,"EOS\n");
break;
case MMAL_EVENT_ERROR:
/* Something went wrong. Signal this to the application */
ctx->status = *(MMAL_STATUS_T *)buffer->data;
fprintf(stderr,"ERROR: %d\n", ctx->status);
break;
default:
break;
}
/* Done with the event, recycle it */
mmal_buffer_header_release(buffer);
}
void input_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
//struct CONTEXT_T *ctx = (struct CONTEXT_T *)port->userdata;
/* The encoder is done with the data, just recycle the buffer header into its pool */
mmal_buffer_header_release(buffer);
}
/** Callback from the output port.
* Buffer has been produced by the port and is available for processing. */
void output_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer )
{
struct CONTEXT_T *ctx = (struct CONTEXT_T *)port->userdata;
/* Queue the decoded video frame */
mmal_queue_put(ctx->queue, buffer);
}
uint8_t ffmpeg_camera_initialize(){
avformat_network_init();
av_register_all();
ret = avformat_open_input(&fmt_ctx, src_filename, NULL, NULL) ;
if (ret <0 ) {
fprintf(stderr, "Could not open source %s\n", src_filename);
return ret;
}
ret = avformat_find_stream_info(fmt_ctx, NULL);
if (ret < 0) {
fprintf(stderr, "Could not find stream information\n");
return ret;
}
ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0); //1 this version creates dec
if (ret < 0) {
fprintf(stderr, "Could not find %s stream in input file '%s'\n",
av_get_media_type_string(AVMEDIA_TYPE_VIDEO), src_filename);
return ret;
} else {
int stream_idx = ret;
st = fmt_ctx->streams[stream_idx];
dec_ctx = avcodec_alloc_context3(NULL);
if (!dec_ctx) {
fprintf(stderr, "Failed to allocate codec\n");
return AVERROR(EINVAL);
}
ret = avcodec_parameters_to_context(dec_ctx, st->codecpar);
if (ret < 0) {
fprintf(stderr, "Failed to copy codec parameters to codec context\n");
return ret;
}
video_stream_idx = stream_idx;
video_stream = fmt_ctx->streams[video_stream_idx];
video_dec_ctx = dec_ctx;
av_dump_format(fmt_ctx, 0, src_filename, 0);
if (!video_stream) {
fprintf(stderr, "Could not find video stream in the input, aborting\n");
ret = 1;
}
}
return ret;
};
uint8_t run(AVPacket *packet, JPEG_T *outjpeg)
{
MMAL_BUFFER_HEADER_T *buffer;
if (input_port) {
if ((buffer = mmal_queue_get(pool_in->queue)) != NULL)
{
memcpy(buffer->data,packet->data,packet->size);
buffer->length=packet->size;
uint8_t nal_type=buffer->data[4] & 0x1f;
fprintf(stderr,"NAL TYPE --------------------> %d\n",nal_type);
//PRINT THE BUFFER CONTENTS
for (uint32_t i = 0; ((i < buffer->length) && (i < 20)); ++i) {
fprintf(stderr, "\\%02x", (unsigned char)buffer->data[i]);
}
fprintf(stderr,"\n");
buffer->pts = buffer->dts = MMAL_TIME_UNKNOWN;
buffer->flags=packet->flags;
fprintf(stderr, "%s sending packet >>>>> %i bytes\n", decoder->input[0]->name, (int)buffer->length);
status = mmal_port_send_buffer(input_port, buffer);
CHECK_STATUS(status, "failed to send buffer to input port");
}
}
if (output_port) {
while ((buffer = mmal_queue_get(context.queue)) != NULL)
{
fprintf(stderr, "%s receiving %d bytes <<<<< frame\n", output_port->name, buffer->length);
if (!buffer->cmd) {
//SEND TO HARDWARE JPEG ENCODER
if (outjpeg) {
jpeg_request.input_size = buffer->length;
jpeg_request.input = buffer->data;
jpeg_request.output=outjpeg->data;
jpeg_request.output_alloc_size = outjpeg->buffer_length;
fprintf(stderr,"OUT SIZE %d", outjpeg->buffer_length);
outjpeg->data_size=0;
if (brcmjpeg_process(jcoder, &jpeg_request) > 0 ) {
fprintf(stderr," Error creating JPEG\n");
} else {
fprintf(stderr," JPEG created %d\n",jpeg_request.output_size);
outjpeg->data_size=jpeg_request.output_size;
}
}
}
mmal_buffer_header_release(buffer);
}
while ((buffer = mmal_queue_get(pool_out->queue)) != NULL)
{
status = mmal_port_send_buffer(output_port, buffer);
CHECK_STATUS(status, "failed to send buffer to output port");
}
}
return status;
}
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "Usage: %s rtsp://<user>:<pass>@url\n", argv[0]);
return 1;
}
src_filename=argv[1];
bcm_host_init();
//Initialize ffmpeg
ffmpeg_camera_initialize();
uint16_t iwidth=video_dec_ctx->width;
uint16_t iheight=video_dec_ctx->height;
//DECODER SETUP
status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &decoder);
CHECK_STATUS(status, "failed to create decoder");
// CONTROL PORT SETTINGS
decoder->control->userdata = (MMAL_PORT_USERDATA_T *)&context;
status = mmal_port_enable(decoder->control, control_callback);
CHECK_STATUS(status, "failed to enable decoder control port");
//INPUT PORT SETTINGS
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(iwidth, 32);
format_in->es->video.height = VCOS_ALIGN_UP(iheight,16);
format_in->es->video.frame_rate.num = 30;
format_in->es->video.frame_rate.den = 1;
format_in->es->video.par.num = 1;
format_in->es->video.par.den = 1;
format_in->es->video.crop.width = iwidth;
format_in->es->video.crop.height = iheight;
//ALLOCATE Extradata, copying from avcodec context
status = mmal_format_extradata_alloc(format_in, video_dec_ctx->extradata_size);
fprintf(stderr,"FORMAT pack size %d\n", video_dec_ctx->extradata_size);
format_in->extradata_size = video_dec_ctx->extradata_size;
if (format_in->extradata_size)
memcpy(format_in->extradata, video_dec_ctx->extradata, video_dec_ctx->extradata_size);
//PRINT the contents of extradata
fprintf(stderr,"Extradata\n");
for (size_t i = 0; i != format_in->extradata_size+1; ++i) {
fprintf(stderr, "\\%02x", (unsigned char)format_in->extradata[i]);
}
fprintf(stderr,"\n");
/* Display the input port format */
fprintf(stderr,"---------------------------------------------------\n");
fprintf(stderr, "INPUT %s\n", decoder->input[0]->name);
fprintf(stderr, " type: %i, fourcc: %4.4s\n", format_in->type, (char *)&format_in->encoding);
fprintf(stderr, " bitrate: %i, framed: %i\n", format_in->bitrate,
!!(format_in->flags & MMAL_ES_FORMAT_FLAG_FRAMED));
fprintf(stderr, " extra data: %i, %p\n", format_in->extradata_size, format_in->extradata);
fprintf(stderr, " width: %i, height: %i, (%i,%i,%i,%i)\n",
format_in->es->video.width, format_in->es->video.height,
format_in->es->video.crop.x, format_in->es->video.crop.y,
format_in->es->video.crop.width, format_in->es->video.crop.height);
MMAL_PARAMETER_BOOLEAN_T error_concealment;
error_concealment.hdr.id = MMAL_PARAMETER_VIDEO_DECODE_ERROR_CONCEALMENT;
error_concealment.hdr.size = sizeof(MMAL_PARAMETER_BOOLEAN_T);
error_concealment.enable = MMAL_FALSE;
status = mmal_port_parameter_set(decoder->input[0], &error_concealment.hdr);
CHECK_STATUS(status, "failed to set error concealment");
decoder->input[0]->buffer_num = decoder->input[0]->buffer_num_recommended;
decoder->input[0]->buffer_size = decoder->input[0]->buffer_size_recommended;
fprintf(stderr,"DECODER INPUT BUFFER SIZE %d NUM %d\n",decoder->input[0]->buffer_size,decoder->input[0]->buffer_num_recommended);
status = mmal_port_format_commit(decoder->input[0]);
CHECK_STATUS(status, "failed to commit decoder input format");
//OUTPUT PORT SETTINGS
MMAL_ES_FORMAT_T *format_out = decoder->output[0]->format;
format_out->type = MMAL_ES_TYPE_VIDEO;
format_out->encoding = MMAL_ENCODING_I420;
format_out->es->video.width = VCOS_ALIGN_UP(iwidth, 32);
format_out->es->video.height = VCOS_ALIGN_UP(iheight,16);
format_out->es->video.frame_rate.num = 30;
format_out->es->video.frame_rate.den = 1;
format_out->es->video.par.num = 0;
format_out->es->video.par.den = 1;
format_out->es->video.crop.width = iwidth;
format_out->es->video.crop.height = iheight;
/* Display the output port format */
fprintf(stderr,"---------------------------------------------------\n");
fprintf(stderr, "OUTPUT %s\n", decoder->output[0]->name);
fprintf(stderr, " type: %i, fourcc: %4.4s\n", format_out->type, (char *)&format_out->encoding);
fprintf(stderr, " bitrate: %i, framed: %i\n", format_out->bitrate,
!!(format_out->flags & MMAL_ES_FORMAT_FLAG_FRAMED));
fprintf(stderr, " extra data: %i, %p\n", format_out->extradata_size, format_out->extradata);
fprintf(stderr, " width: %i, height: %i, (%i,%i,%i,%i)\n",
format_out->es->video.width, format_out->es->video.height,
format_out->es->video.crop.x, format_out->es->video.crop.y,
format_out->es->video.crop.width, format_out->es->video.crop.height);
decoder->output[0]->buffer_num = decoder->output[0]->buffer_num_recommended;
decoder->output[0]->buffer_size = decoder->output[0]->buffer_size_recommended;
fprintf(stderr,"DECODER OUTPUT BUFFER SIZE %d NUM %d\n",decoder->output[0]->buffer_size_recommended,decoder->output[0]->buffer_num_recommended);
status = mmal_port_format_commit(decoder->output[0]);
CHECK_STATUS(status, "failed to commit decoder output format");
status = mmal_component_enable(decoder);
CHECK_STATUS(status, "failed to enable decoder output format");
//RGB coder
status = mmal_component_create("vc.ril.isp", &rgbcoder);
CHECK_STATUS(status, "failed to create isp");
// CONTROL PORT SETTINGS
rgbcoder->control->userdata = (MMAL_PORT_USERDATA_T *)&context;
status = mmal_port_enable(rgbcoder->control, control_callback);
CHECK_STATUS(status, "failed to enable control port");
//INPUT PORT SETTINGS
format_in = rgbcoder->input[0]->format;
format_in->type = MMAL_ES_TYPE_VIDEO;
format_in->encoding = MMAL_ENCODING_I420;
format_in->es->video.width = VCOS_ALIGN_UP(iwidth, 32);
format_in->es->video.height = VCOS_ALIGN_UP(iheight,16);
format_in->es->video.frame_rate.num = 30;
format_in->es->video.frame_rate.den = 1;
format_in->es->video.par.num = 1;
format_in->es->video.par.den = 1;
format_in->es->video.crop.width = iwidth;
format_in->es->video.crop.height = iheight;
/* Display the input port format */
fprintf(stderr,"---------------------------------------------------\n");
fprintf(stderr, "INPUT %s\n", rgbcoder->input[0]->name);
fprintf(stderr, " type: %i, fourcc: %4.4s\n", format_in->type, (char *)&format_in->encoding);
fprintf(stderr, " bitrate: %i, framed: %i\n", format_in->bitrate,
!!(format_in->flags & MMAL_ES_FORMAT_FLAG_FRAMED));
fprintf(stderr, " extra data: %i, %p\n", format_in->extradata_size, format_in->extradata);
fprintf(stderr, " width: %i, height: %i, (%i,%i,%i,%i)\n",
format_in->es->video.width, format_in->es->video.height,
format_in->es->video.crop.x, format_in->es->video.crop.y,
format_in->es->video.crop.width, format_in->es->video.crop.height);
rgbcoder->input[0]->buffer_num = rgbcoder->input[0]->buffer_num_recommended;
rgbcoder->input[0]->buffer_size = rgbcoder->input[0]->buffer_size_recommended;
fprintf(stderr,"RGBCODER INPUT BUFFER SIZE %d NUM %d\n",rgbcoder->input[0]->buffer_size,rgbcoder->input[0]->buffer_num_recommended);
status = mmal_port_format_commit(rgbcoder->input[0]);
CHECK_STATUS(status, "failed to commit rgbcoder input format");
//OUTPUT PORT SETTINGS
format_out = rgbcoder->output[0]->format;
format_out->type = MMAL_ES_TYPE_VIDEO;
format_out->encoding = MMAL_ENCODING_RGBA;
format_out->es->video.width = VCOS_ALIGN_UP(iwidth, 32);
format_out->es->video.height = VCOS_ALIGN_UP(iheight,16);
format_out->es->video.frame_rate.num = 30;
format_out->es->video.frame_rate.den = 1;
format_out->es->video.par.num = 0;
format_out->es->video.par.den = 1;
format_out->es->video.crop.width = iwidth;
format_out->es->video.crop.height = iheight;
/* Display the output port format */
fprintf(stderr,"---------------------------------------------------\n");
fprintf(stderr, "OUTPUT %s\n", rgbcoder->output[0]->name);
fprintf(stderr, " type: %i, fourcc: %4.4s\n", format_out->type, (char *)&format_out->encoding);
fprintf(stderr, " bitrate: %i, framed: %i\n", format_out->bitrate,
!!(format_out->flags & MMAL_ES_FORMAT_FLAG_FRAMED));
fprintf(stderr, " extra data: %i, %p\n", format_out->extradata_size, format_out->extradata);
fprintf(stderr, " width: %i, height: %i, (%i,%i,%i,%i)\n",
format_out->es->video.width, format_out->es->video.height,
format_out->es->video.crop.x, format_out->es->video.crop.y,
format_out->es->video.crop.width, format_out->es->video.crop.height);
rgbcoder->output[0]->buffer_num = rgbcoder->output[0]->buffer_num_recommended;
rgbcoder->output[0]->buffer_size = rgbcoder->output[0]->buffer_size_recommended;
fprintf(stderr,"RGBCODER OUTPUT BUFFER SIZE %d NUM %d\n",rgbcoder->output[0]->buffer_size_recommended,rgbcoder->output[0]->buffer_num_recommended);
status = mmal_port_format_commit(rgbcoder->output[0]);
CHECK_STATUS(status, "failed to commit rgbcoder output format");
status = mmal_component_enable(rgbcoder);
CHECK_STATUS(status, "failed to enable rgbcoder output format");
//CONNECT DECODER TO RGBCODER
connect_ports(decoder->output[0],rgbcoder->input[0],&connection);
pool_in = mmal_pool_create(decoder->input[0]->buffer_num,
decoder->input[0]->buffer_size);
pool_out = mmal_pool_create(rgbcoder->output[0]->buffer_num,
rgbcoder->output[0]->buffer_size);
decoder->input[0]->userdata = (struct MMAL_PORT_USERDATA_T *)&context;
if ( mmal_port_enable(decoder->input[0], input_callback) != MMAL_SUCCESS ) {
fprintf(stderr,"failed to enable decoder input port");
}
rgbcoder->output[0]->userdata = (struct MMAL_PORT_USERDATA_T *)&context;
if ( mmal_port_enable(rgbcoder->output[0], output_callback) != MMAL_SUCCESS ) {
fprintf(stderr,"failed to enable rgbcoder output port");
}
context.queue = mmal_queue_create();
input_port=decoder->input[0];
output_port=rgbcoder->output[0];
//JPEG ENCODER
if (brcmjpeg_create(BRCMJPEG_TYPE_ENCODER, &jcoder) > 0)
fprintf(stderr,"Failed to create jpeg encoder");
jpeg_request.input_size = 0;
jpeg_request.width = format_out->es->video.width;
jpeg_request.height = format_out->es->video.height;
jpeg_request.quality = 10;
jpeg_request.pixel_format = PIXEL_FORMAT_RGBA;
JPEG_T jpeg_result(format_out->es->video.width*format_out->es->video.height*24);
fprintf(stderr,"JPEG BUFFER length %d, data_size %lu \n",jpeg_result.buffer_length,jpeg_result.data_size);
FILE *JPEGFile;
char JPEGFName[256];
std::mutex m_jcoder;
//PAUSE
fprintf(stderr,"Press Enter to Continue\n");
fprintf(stderr,"Press Enter again to stop\n");
getchar();
/* Start decoding */
fprintf(stderr, "start decoding\n");
int framecount=0;
char c;
int n, tem;
tem = fcntl(0, F_GETFL, 0);
fcntl (0, F_SETFL, (tem | O_NDELAY));
av_init_packet(&pkt);
//Send the sps and pps as first packet
pkt.data=(uint8_t*)av_mallocz(video_dec_ctx->extradata_size);
memcpy(pkt.data,video_dec_ctx->extradata,video_dec_ctx->extradata_size);
pkt.size=video_dec_ctx->extradata_size;
run(&pkt,NULL);
av_packet_unref(&pkt);
//Send the rest of the packets in a loop
while ((av_read_frame(fmt_ctx, &pkt) >= 0) && (framecount <500 )) {
if (pkt.stream_index == video_stream_idx) {
run(&pkt,&jpeg_result);
m_jcoder.lock(); //lock outbuffer then read
if (jpeg_result.data_size) {
sprintf(JPEGFName, "frame%d.jpg", framecount);
JPEGFile = fopen(JPEGFName, "wb");
fwrite(jpeg_result.data, 1, jpeg_result.data_size, JPEGFile);
fclose(JPEGFile);
}
m_jcoder.unlock();
av_packet_unref(&pkt);
}
framecount++;
fprintf(stderr, "Frame number %d\n",framecount);
n = read(0, &c, 1);
if (n > 0) break;
}
fcntl(0, F_SETFL, tem);
/* End decoding */
fprintf(stderr, "end decoding\n");
avformat_network_deinit();
if (video_dec_ctx)
avcodec_free_context(&video_dec_ctx);
if (fmt_ctx)
avformat_close_input(&fmt_ctx);
mmal_port_disable(decoder->input[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(rgbcoder->output[0]);
mmal_port_disable(rgbcoder->control);
mmal_port_flush(rgbcoder->input[0]);
mmal_port_flush(rgbcoder->output[0]);
mmal_port_flush(rgbcoder->control);
mmal_connection_disable(connection);
if (connection)
mmal_connection_destroy(connection);
if (decoder)
mmal_component_destroy(decoder);
if (rgbcoder)
mmal_component_destroy(rgbcoder);
if (pool_in)
mmal_pool_destroy(pool_in);
if (pool_out)
mmal_pool_destroy(pool_out);
if (context.queue)
mmal_queue_destroy(context.queue);
}