[vlc-devel] Problem with an access_demux module

Christophe Démaret vlc at cdemaret.com
Sat Jun 3 14:24:03 CEST 2006


Hi there

I'm currently trying to build an access_demux module for a hardware
MJPEG2000 card (no v4l).
For now, I'm just trying to read some data from the kernel-mode driver.
So basically:
1) ask the driver what the size of the frame will be (ioctl() )
2) ask VLC to give me an empty packet
3) copy the data from the driver into the packet (read() )

He is the code (based on screen.c more or less):

---BEGIN---


/*****************************************************************************
  * Module descriptor

*****************************************************************************/

static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );

vlc_module_begin();
     set_shortname( _("Imax") );
     set_description( _("Imax MJPEG2000 card") );
     set_category( CAT_INPUT );
     set_subcategory( SUBCAT_INPUT_ACCESS );
     add_integer( "imax-caching", DEFAULT_PTS_DELAY / 1000, NULL,
                  CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
     add_float( "imax-fps", IMAX_FPS, NULL, FPS_TEXT, FPS_LONGTEXT,
VLC_TRUE );
     add_integer( "imax-id", 0, NULL, ID_TEXT, ID_LONGTEXT, VLC_TRUE );
     add_shortcut( "imax" );
     set_capability( "access_demux", 10 );
     set_callbacks( Open, Close );
vlc_module_end();

/*****************************************************************************
  * Local prototypes

*****************************************************************************/
static int Control( demux_t *, int, va_list );
static int Demux  ( demux_t * );

struct demux_sys_t
{
     es_format_t fmt;
     es_out_id_t *es;

     float f_fps;
     mtime_t i_next_date;
     int i_incr;

    int fd;
};

/*****************************************************************************
  * DemuxOpen:

*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
     demux_t     *p_demux = (demux_t*)p_this;
     demux_sys_t *p_sys;
     vlc_value_t val;

     /* Fill p_demux field */
     /* callbacks*/
     p_demux->pf_demux = Demux;
     p_demux->pf_control = Control;
     p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
     memset( p_sys, 0, sizeof( demux_sys_t ) );

     msg_Dbg( p_demux, "opening imax access_demux");

     var_Create( p_demux, "imax-caching",
VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
     var_Create( p_demux, "imax-fps", VLC_VAR_FLOAT|VLC_VAR_DOINHERIT );
     var_Get( p_demux, "imax-fps", &val );
     p_sys->f_fps = val.f_float;
     p_sys->i_incr = 1000000 / val.f_float;
     p_sys->i_next_date = 0;

    /* open the device */
    p_sys->fd = open(IMAX_DEVICE_PATH, O_RDWR);
    if( p_sys->fd == -1)
    {
         msg_Err( p_demux, "failed to open the device");
         return VLC_EGENERIC;
    }
    else
    {
       msg_Dbg( p_demux, "device %s sucessfully opened", IMAX_DEVICE_PATH);
    }

#if 0
     if( screen_InitCapture( p_demux ) != VLC_SUCCESS )
     {
         free( p_sys );
         return VLC_EGENERIC;
     }
     msg_Dbg( p_demux, "screen width: %i, height: %i, depth: %i",
              p_sys->fmt.video.i_width, p_sys->fmt.video.i_height,
              p_sys->fmt.video.i_bits_per_pixel );
#endif

     p_sys->es = es_out_Add( p_demux->out, &p_sys->fmt );
     msg_Dbg( p_demux, "es_out_Add done, ex = %x (%d)", p_sys->es,
p_sys->es);

/*   es_format_Init( &p_sys->fmt, VIDEO_ES, VLC_FOURCC('m','p','2','c')
);   */

/* For TS muxing, trying to make the TS muxer believe that i'm using a
supported format.
    This should be OK as long as I don't try to decode it ! This is only
for test purpose */

    es_format_Init( &p_sys->fmt, VIDEO_ES, VLC_FOURCC('M','J','P','G') );

     return VLC_SUCCESS;
}

/*****************************************************************************
  * Close:

*****************************************************************************/
static void Close( vlc_object_t *p_this )
{
     demux_t     *p_demux = (demux_t*)p_this;
     demux_sys_t *p_sys = p_demux->p_sys;
#if 0
     screen_CloseCapture( p_demux );
#endif
     close(p_sys->fd);
     free( p_sys );
}

/*****************************************************************************
  * Demux:

*****************************************************************************/
static int Demux( demux_t *p_demux )
{
    int i_size, ret_read;
     demux_sys_t *p_sys = p_demux->p_sys;
     block_t *p_block;

     if( !p_sys->i_next_date ) p_sys->i_next_date = mdate();

     /* Frame skipping if necessary */
     while( mdate() >= p_sys->i_next_date + p_sys->i_incr )
         p_sys->i_next_date += p_sys->i_incr;

     mwait( p_sys->i_next_date );
    msg_Dbg( p_demux, "preparing for ioctl, fd=%d", p_sys->fd);
    i_size = ioctl( p_sys->fd, VJ2C_READSIZE, 0);

    /* allocating the new block */

    msg_Dbg( p_demux, "preparing for block_New, i_size=%d", i_size);

    if( !( p_block = block_New( p_demux, i_size ) ) )
    {
          msg_Err( p_demux, "cannot get block, p_block=%x", p_block );
    }
    else
    {
       /* effectively reading data */
       ret_read=read(p_sys->fd, p_block->p_buffer, i_size);
       if (ret_read == -1)
       {
          msg_Err( p_demux, "failed to read frame");
          free (p_block);
          p_block = NULL;
       }
       else
       {
          msg_Dbg( p_demux, "Everything is fine, block created", p_sys->fd);
       }
    }
     if( !p_block )
     {
       /* allocation failed */
         p_sys->i_next_date += p_sys->i_incr;
         return 1;
     }
     p_block->i_dts = p_block->i_pts = p_sys->i_next_date;
    msg_Dbg( p_demux, "Finished. p_block=%p, date=%lld", p_block,
p_block->i_dts );

     es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_block->i_pts );
     es_out_Send( p_demux->out, p_sys->es, p_block );

     p_sys->i_next_date += p_sys->i_incr;

     return 1;
}

/*****************************************************************************
  * Control:

*****************************************************************************/
static int Control( demux_t *p_demux, int i_query, va_list args )
{
     vlc_bool_t *pb;
     int64_t *pi64;

     switch( i_query )
     {
         /* Special for access_demux */
         case DEMUX_CAN_PAUSE:
         case DEMUX_CAN_CONTROL_PACE:
             /* TODO */
             pb = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
             *pb = VLC_FALSE;
             return VLC_SUCCESS;

         case DEMUX_GET_PTS_DELAY:
             pi64 = (int64_t*)va_arg( args, int64_t * );
             *pi64 = (int64_t)var_GetInteger( p_demux, "imax-caching" )
*1000;
             return VLC_SUCCESS;

         /* TODO implement others */
         default:
             return VLC_EGENERIC;
     }
}


---END---


I know, there is a lot of debug messages but since i'm working with 1
FPS (test speed), i have plenty of time to display infos.
It seems to work: ioctl read the correct size, the packet is created and
the data are copied.
In the end, i have an address for the packet (different each time) and a
DTS, the delta between the DTS being 100000 si 1 sec so it is great.

I can't decode (unsupported format + 120 Mhz ARM ;) ) so i tested it
with --vout dummy. It looks fine: no error. and 1 packet each second.
Then, i decided to try sout/std/file/ts since in the end the aim is
sout/std/udp/ts. I have the debug message, 1 packet per second, etc and
the data is... 0 bytes large :-/
The very same parameters for sout work perfectly with an AVI MJPEG file
so they are not the main problem.

Does anyone here has any idea ? Any clues about the API ? The only info
I have is an old mail from fenrir in the ml and of course the beloved
source code but honnestly it's incredibly hard for me to understand "the
way it works" so the only solution is to look at my neighbor (screen.c
in this case) like we all do/did at school but hmmm it's not that great
when you need to do a little different :-/



I'm not asking you to correct my bugs or whatever, I just would like
some infos about what the access_demux plugin should do.

Currently I'm doing the following:

1) check the size of my frame
2) ask VLC for a new block with that size and checking the return
Code:
p_block = block_New( p_demux, i_size )

3) put the data in *(p_block->p_buffer) using memcpy or similar
Code:
read(p_sys->fd, p_block->p_buffer, i_size);

4) give the backet dates (pts and dts)
Code:
p_block->i_dts = p_block->i_pts = p_sys->i_next_date;

5) send the packet to the ES
Code:
es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_block->i_pts );
es_out_Send( p_demux->out, p_sys->es, p_block );


Could anyone confirm that this is correct ? I mean, i havent seen any
interresting field in the .h. The packet has its data, its date, its
FOURCC (given in open) it should be enought isn't ? The system knows
what to play, when and how.

Could anyone confirm this the "The Right Way" ? We can discuss about the
price of this info, how about a Guiness ? Wink Or even better: a
Bavarian Weissbier delivered at Chatenay !

PS: This is a copy of a topic I posted on the web forums(
http://forum.videolan.org/viewtopic.php?t=21388 ). If you don't mind, I
would prefer an answer on the web forum since i cannot send e-mails nor
access anyway webmails during the day (mo-fr 8-17) although, this is
exactly the place i need those infos). However, if this is a problem,
any help would of course be greatly appreciated :).

-- 
This is the vlc-devel mailing-list, see http://www.videolan.org/vlc/
To unsubscribe, please read http://developers.videolan.org/lists.html



More information about the vlc-devel mailing list