cmisip
Posts: 72
Joined: Tue Aug 25, 2015 12:38 am

I am getting MMAL_EIO (Error 7) when trying to construct a H264 decoder.

Sun Aug 12, 2018 1:08 am

I have avcodec opening an rtsp stream. It reads packets from the stream and then I copy them over to the buffer that I send to the input of the decoder. The extradata is copied over from avcodec as well. The packet returned by avcodec contains NAL units. The first NAL unit contains the SPS, PPS, and IDR separated by 0x0000001. (This repeats periodically ). The subsequent NAL units contained in the avcodec packet contain the start code and the payload. So far as I can tell, the format is correct. The buffer is sent to the decoder, but the decoder does not return any output. It does not like the buffer for some reason. Any Ideas?

Code: Select all

#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 <stdio.h>
#include <fcntl.h>

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 {
   //VCOS_SEMAPHORE_T semaphore;
   MMAL_QUEUE_T *queue=NULL;
   MMAL_STATUS_T status;
} context;

MMAL_STATUS_T status = MMAL_EINVAL;
MMAL_COMPONENT_T *decoder = NULL;
MMAL_POOL_T *pool_in = NULL, *pool_out = NULL;
MMAL_PORT_T *input_port=NULL;
MMAL_PORT_T *output_port=NULL;


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;



/** 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;
   fprintf(stderr,"CONTROL callback called\n");

   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);

   /* Kick the processing thread */
   //vcos_semaphore_post(&ctx->semaphore);
}



void input_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
   //struct CONTEXT_T *ctx = (struct CONTEXT_T *)port->userdata;
   fprintf(stderr,"INPUT callback called\n");
   /* 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;
   fprintf(stderr,"OUTPUT callback called\n");

   /* 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)
{   
	MMAL_BUFFER_HEADER_T *buffer;
	if (input_port) { 
	if ((buffer = mmal_queue_get(pool_in->queue)) != NULL)
      {  
         
         mmal_buffer_header_mem_lock(buffer);
         
         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) {
                    fprintf(stderr, "\\%02x", (unsigned char)buffer->data[i]);
         }           
         fprintf(stderr,"\n");
         
         //buffer->pts=packet->pts;
         //buffer->dts=packet->pts;
         buffer->pts = buffer->dts = MMAL_TIME_UNKNOWN;
         mmal_buffer_header_mem_unlock(buffer);
            
         
         fprintf(stderr, "%s sending packet >>>>> %i bytes\n", decoder->input[0]->name, (int)buffer->length);
         status = mmal_port_send_buffer(decoder->input[0], buffer);
         CHECK_STATUS(status, "failed to send buffer to input port");
      }

      }
      
      if (output_port) {
      while ((buffer = mmal_queue_get(context.queue)) != NULL)
      {
         mmal_buffer_header_mem_lock(buffer);
         
         fprintf(stderr, "%s receiving %d bytes <<<<< frame\n", decoder->output[0]->name, buffer->length);
         
         mmal_buffer_header_mem_unlock(buffer); 
         mmal_buffer_header_release(buffer);
      }

     
      while ((buffer = mmal_queue_get(pool_out->queue)) != NULL)
      {
         status = mmal_port_send_buffer(decoder->output[0], 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];
     
   //Initialize ffmpeg
   ffmpeg_camera_initialize();
    
   uint16_t iwidth=video_dec_ctx->width;
   uint16_t iheight=video_dec_ctx->height; 
   
   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 control port");
   
   
   //INPUT PORT SETTINGS
   input_port=decoder->input[0];
   
   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");    
              
       
   
   status = mmal_port_format_commit(decoder->input[0]);
   CHECK_STATUS(status, "failed to commit input format");  

   status = mmal_port_enable(decoder->input[0], input_callback);
   CHECK_STATUS(status, "failed to enable input port");	
   
   decoder->input[0]->buffer_num = decoder->input[0]->buffer_num_recommended;
   decoder->input[0]->buffer_size = decoder->input[0]->buffer_size_recommended;
   
   pool_in = mmal_pool_create(decoder->input[0]->buffer_num,
                              decoder->input[0]->buffer_size);
   

   // Store a reference to our context in each port (will be used during callbacks) */
   decoder->input[0]->userdata = (MMAL_PORT_USERDATA_T *)&context;
   
   
   
   //OUTPUT PORT SETTINGS
   
   MMAL_ES_FORMAT_T *format_out = decoder->output[0]->format;
   
   output_port=decoder->output[0];
    
   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);
          
   
   
   status = mmal_port_format_commit(decoder->output[0]);
   CHECK_STATUS(status, "failed to commit output format");   
   
   status = mmal_port_enable(decoder->output[0], output_callback);
   CHECK_STATUS(status, "failed to enable output port"); 
   
   
   context.queue = mmal_queue_create();
	
   decoder->output[0]->buffer_num = decoder->output[0]->buffer_num_recommended;
   decoder->output[0]->buffer_size = decoder->output[0]->buffer_size_recommended; 
   
   pool_out = mmal_pool_create(decoder->output[0]->buffer_num,
                               decoder->output[0]->buffer_size);
                               
   decoder->output[0]->userdata = (MMAL_PORT_USERDATA_T *)&context; 
   
 
   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);
    
     
   //Loop   
   while (av_read_frame(fmt_ctx, &pkt) >= 0) {
        if (pkt.stream_index == video_stream_idx) {
            
            run(&pkt);
            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->output[0]);
   mmal_port_disable(decoder->control); 
      
   mmal_port_flush(decoder->input[0]);
   mmal_port_flush(decoder->output[0]);
   mmal_port_flush(decoder->control); 
      
      
   if (decoder)
      mmal_component_destroy(decoder);
   if (pool_in)
      mmal_pool_destroy(pool_in);
   if (pool_out)
      mmal_pool_destroy(pool_out);
   if (context.queue)
      mmal_queue_destroy(context.queue);
    
     
}

Here is a sample output:

Code: Select all

Input #0, rtsp, from 'rtsp://01432721:admin:[email protected]/onvif2':
  Metadata:
    title           : H.264 Video, RtspServer_0.0.0.2
  Duration: N/A, start: 0.100000, bitrate: N/A
    Stream #0:0: Video: h264 (Baseline), yuv420p(progressive), 320x192, 10 fps, 10 tbr, 90k tbn, 180k tbc
FORMAT pack size 22
Extradata
\00\00\00\01\67\42\00\1f\95\a8\14\01\6e\40\00\00\00\01\68\ce\3c\80\00
---------------------------------------------------
INPUT vc.ril.video_decode:in:0
 type: 3, fourcc: H264
 bitrate: 0, framed: 0
 extra data: 22, 0x1824fd8
 width: 320, height: 192, (0,0,320,192)
---------------------------------------------------
OUTPUT vc.ril.video_decode:out:0
 type: 3, fourcc: I420
 bitrate: 0, framed: 0
 extra data: 0, (nil)
 width: 320, height: 192, (0,0,320,192)
Press Enter to Continue
Press Enter again to stop
start decoding
NAL TYPE --------------------> 7
\00\00\00\01\67\42\00\1e\f4\0a\0c\c8\00\00\00\01\68\ce\3c\80\00\00\00\01\65\88\84\05\fc\71\c6\3f\62\85\00\02\70\54\b3\d5\01\f0\3d\46\6c\0b\aa\6f\23\7e\57\64\b9\0f\00\0b\09\fd\b5\c8\3a\80\ca\83\88\26\d6\53\36\1f\e0\31\5a\60\13\e4\3d\8b\ca\53\6f\3f\7f\72\37\90\ed\85\c4\5a\43\b1\5e\f3\93\33\62\25\63\b3\97\ff\ff\40\93\9c\5b\a1\0c\01\d0\05\9f\4a\16\e7\41\77\fc\1e\d4\b3\fa\49\ae\00\27\2f\e2\70\b3\09\cf\bf\c9\cb\e4\3d\a9\68\04\52\19\25\a9\03\59\b3\2c\81\0b\6f\70\86\62\8c\87\74\0f\3b\d7\bb\5f\65\38\7c\e4\c7\f9\1b\b1\d8\a5\18\78\c9\9e\31\8c\4e\43\22\8d\ec\11\cb\33\e8\14\8f\c0\4a\c5\2d\67\ee\d6\a0\e2\1b\e4\c2\5c\3f\d6\e7\f8\cc\12\a7\99\c0\0a\07\bf\cf\e4\1d\40\71\61\a2\3f\ea\2b\31\bb\f0\16\7a\24\c2\f7\0f\6f\67\05\bc\22\20\2d\21\ff\ff\b3\f7\cc\b4\3c\85\28\d4\9b\7b\fc\59\b2\1b\47\2b\f3\73\fe\f8\9f\77\f5\02\c4\63\86\10\90\8d\fa\ae\2d\08\85\30\1b\be\66\1c\67\20\0f\9a\ad\83\2c\b9\91\80\8a\44\32\45\0e\2a\30\fd\2d\97\cc\ce\cc\87\74\0b\3b\de\51\bb\5b\97\69\30\32\03\ed\bf\d4\f3\10\58\89\46\..............//This is quite long due to IDR.
vc.ril.video_decode:in:0(H264) sending packet >>>>> 3604 bytes
CONTROL callback called
ERROR: 7
INPUT callback called
Frame number 1
NAL TYPE --------------------> 1
\00\00\00\01\41\9a\24\0d\cf\c6\0a\81\db\2c\e0\0a\f2\4c\37\21\69\a2\e9\86\eb\3c\d9\e3\fe\7f\be\02\d0\d3\ae\6c\66\3f\41\7f\ff\45\73\34\47\0a\76\76\98\bc\3f\8c\98\e6\1e\49\cd\00\e3\c5\ff\50\00\61\1e\91\82\5f\8e\64\6c\92\f9\f9\e8\fe\0b\c4\42\ae\05\dc\74\0b\b0
vc.ril.video_decode:in:0(H264) sending packet >>>>> 85 bytes
Frame number 2
NAL TYPE --------------------> 1
\00\00\00\01\41\9a\48\09\d7\28\a1\e4\df\9b\89\b0\70\ca\59\de\37\01\e0\31\26\71\d1\18\5e
vc.ril.video_decode:in:0(H264) sending packet >>>>> 29 bytes
mmal: mmal_port_send_buffer: vc.ril.video_decode:in:0(H264): send failed: ENOMEM
failed to send buffer to input port
Frame number 3
NAL TYPE --------------------> 1
\00\00\00\01\41\9a\6c\17\3f\16\28\7d\0d\48\1c\0f\99\e9\93\a0\7a\5e\27\96\05\3f\87\e0\b8\78\08\7e\ca\5c\e1\6e\55\29\8d\27\79\ab\0a\51\60\8f\95\3b\e3\20
vc.ril.video_decode:in:0(H264) sending packet >>>>> 50 bytes
mmal: mmal_port_send_buffer: vc.ril.video_decode:in:0(H264): send failed: ENOMEM
failed to send buffer to input port
Frame number 4
NAL TYPE --------------------> 1
\00\00\00\01\41\9a\90\3d\70\80\a3\5c\91\7c\8b\b3\e6\1f\21\bd\77\5d\7c\dd\43\b3\fd\f8\8a\ad\57\22\fe\33\29\18\4a\c0\dc\15\09\48\ad\0c\73\e1\86\2d\43\b4\30\1d\44\40\a1\75\c6\61\bc\b9\9f\19\09\58\aa\ec\e2\c0\fb\6f\1d\f3\4d\2a\cf\e8\6d\7d\4c\5e\55\3b
CONTROL callback called
ERROR: 7

Every now and then I also get ENOMEM.

Thanks,
Chris

cmisip
Posts: 72
Joined: Tue Aug 25, 2015 12:38 am

Re: I am getting MMAL_EIO (Error 7) when trying to construct a H264 decoder.

Wed Aug 15, 2018 12:50 am

So I got this working. I am getting output buffers in the correct size. The stumbling blocks were:

1. The sps and pps data must be sent in band as the first packet to the decoder. I had assumed that setting the input format's extradata fields were enough but that was not the case.

2. The input and output ports have to be enabled AFTER the input and output pools are created. Otherwise you get an ENOMEM.


A question for the devs.
1. Do I need to worry about splitting packets for the encoder and marking the first packet as MMAL_BUFFER_HEADER_FLAG_FRAME_START and the last packet as MMAL_BUFFER_HEADER_FLAG_FRAME_END? I have not encountered this situation. I expect it might happen when the resolution of the video frame is high enough that the packet size is greater than input buffer size.

Here is the working code for anyone going down this road

Thanks,
Chris

Code: Select all

#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 <stdio.h>
#include <fcntl.h>

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 {
   //VCOS_SEMAPHORE_T semaphore;
   MMAL_QUEUE_T *queue=NULL;
   MMAL_STATUS_T status;
} context;

MMAL_STATUS_T status = MMAL_EINVAL;
MMAL_COMPONENT_T *decoder = NULL;
MMAL_POOL_T *pool_in = NULL, *pool_out = NULL;
MMAL_PORT_T *input_port=NULL;
MMAL_PORT_T *output_port=NULL;


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;



/** 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;
   fprintf(stderr,"CONTROL callback called\n");

   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);

   /* Kick the processing thread */
   //vcos_semaphore_post(&ctx->semaphore);
}



void input_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
   //struct CONTEXT_T *ctx = (struct CONTEXT_T *)port->userdata;
   fprintf(stderr,"INPUT callback called\n");
   /* 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;
   fprintf(stderr,"OUTPUT callback called\n");

   /* 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)
{   
	MMAL_BUFFER_HEADER_T *buffer;
	if (input_port) { 
	if ((buffer = mmal_queue_get(pool_in->queue)) != NULL)
      {  
         
         mmal_buffer_header_mem_lock(buffer);
         
         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;
         mmal_buffer_header_mem_unlock(buffer);
            
         
         fprintf(stderr, "%s sending packet >>>>> %i bytes\n", decoder->input[0]->name, (int)buffer->length);
         status = mmal_port_send_buffer(decoder->input[0], buffer);
         CHECK_STATUS(status, "failed to send buffer to input port");
      }

      }
      
      if (output_port) {
      while ((buffer = mmal_queue_get(context.queue)) != NULL)
      {
         mmal_buffer_header_mem_lock(buffer);
         
         fprintf(stderr, "%s receiving %d bytes <<<<< frame\n", decoder->output[0]->name, buffer->length);
         
         mmal_buffer_header_mem_unlock(buffer); 
         mmal_buffer_header_release(buffer);
      }

     
      while ((buffer = mmal_queue_get(pool_out->queue)) != NULL)
      {
         status = mmal_port_send_buffer(decoder->output[0], 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; 
   
   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 control port");
   
   
   //INPUT PORT SETTINGS
   input_port=decoder->input[0];
   
   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 input format");  

   status = mmal_port_enable(decoder->input[0], input_callback);
   CHECK_STATUS(status, "failed to enable input port");	
   
   pool_in = mmal_pool_create(decoder->input[0]->buffer_num,
                              decoder->input[0]->buffer_size);
   

   // Store a reference to our context in each port (will be used during callbacks) */
   decoder->input[0]->userdata = (MMAL_PORT_USERDATA_T *)&context;
   
   
   
   //OUTPUT PORT SETTINGS
   
   MMAL_ES_FORMAT_T *format_out = decoder->output[0]->format;
   
   output_port=decoder->output[0];
    
   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);
          
  
   context.queue = mmal_queue_create();
	
   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 output format");   
   
   status = mmal_port_enable(decoder->output[0], output_callback);
   CHECK_STATUS(status, "failed to enable output port"); 
   
   pool_out = mmal_pool_create(decoder->output[0]->buffer_num,
                               decoder->output[0]->buffer_size);
                               
   decoder->output[0]->userdata = (MMAL_PORT_USERDATA_T *)&context; 
   
   
   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);
   av_packet_unref(&pkt);
   
   //Send the rest of the packets in a loop
   while ((av_read_frame(fmt_ctx, &pkt) >= 0) && (framecount <100 )) {
        if (pkt.stream_index == video_stream_idx) {
           
            run(&pkt);
            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->output[0]);
   mmal_port_disable(decoder->control); 
      
   mmal_port_flush(decoder->input[0]);
   mmal_port_flush(decoder->output[0]);
   mmal_port_flush(decoder->control); 
      
      
   if (decoder)
      mmal_component_destroy(decoder);
   if (pool_in)
      mmal_pool_destroy(pool_in);
   if (pool_out)
      mmal_pool_destroy(pool_out);
   if (context.queue)
      mmal_queue_destroy(context.queue);
    
     
}


6by9
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 5357
Joined: Wed Dec 04, 2013 11:27 am
Location: ZZ9 Plural Z Alpha, aka just outside Cambridge.

Re: I am getting MMAL_EIO (Error 7) when trying to construct a H264 decoder.

Wed Aug 15, 2018 7:02 am

cmisip wrote:
Wed Aug 15, 2018 12:50 am
So I got this working. I am getting output buffers in the correct size. The stumbling blocks were:

1. The sps and pps data must be sent in band as the first packet to the decoder. I had assumed that setting the input format's extradata fields were enough but that was not the case.
extradata should work and I thought was used by either OMXPlayer or Kodi. It's not one I really want to expend a lot of time investigating though.
cmisip wrote:2. The input and output ports have to be enabled AFTER the input and output pools are created. Otherwise you get an ENOMEM.
AFAIK That shouldn't be required, although I do tend to always create pools first so that everything is ready before enabling processing.
cmisip wrote:A question for the devs.
1. Do I need to worry about splitting packets for the encoder and marking the first packet as MMAL_BUFFER_HEADER_FLAG_FRAME_START and the last packet as MMAL_BUFFER_HEADER_FLAG_FRAME_END? I have not encountered this situation. I expect it might happen when the resolution of the video frame is high enough that the packet size is greater than input buffer size.
I assume that is a typo and you mean decoder (not encoder). (The encoder always requires full video frames to encode)
The decoder is quite happy with a random lump of an ES with startcodes, but if you provide frame end flags then it will reduce the latency by kicking the bitstream decoder earlier.
Software Engineer at Raspberry Pi Trading. Views expressed are still personal views.
Please don't send PMs asking for support - use the forum.
I'm not interested in doing contracts for bespoke functionality - please don't ask.

cmisip
Posts: 72
Joined: Tue Aug 25, 2015 12:38 am

Re: I am getting MMAL_EIO (Error 7) when trying to construct a H264 decoder.

Thu Aug 16, 2018 3:47 am

Thanks 6by9. I did mean decoder but I was thinking about the encoder as well. I really appreciate the time you spend answering these questions.

Chris

Return to “Advanced users”

Who is online

Users browsing this forum: No registered users and 9 guests