[theora-dev] V4L + Theora small app...

Mat heavensdoor78 at gmail.com
Fri Oct 6 00:42:22 PDT 2006


Hi all.
I post a small app I made that create 2 clips using a V4L device.
I would like to get every tips you think it's useful...

Some notes:
- I make 2 clips because in our project we create series of clips and 
it's important to check that the all the resources ( memory, 
descriptors, etc. ) are freed correctly and to reuse all the resources 
that can be reused
- the clips are not well syncronized for now... I mean that the clip go 
on playing for some seconds after the ending of the clip (in Windows 
mplayer2, perhaps with an old codec plugin, I don't remember). Problems 
with the timestamps of the frames I suppose, but I don't know how to set 
them in OGG/Theora
- my first goal ( after stability of the system of course... ) is to 
reduce the CPU load ( as told in a previous post ) and to use less 
resources as possible

Greetings,
-Mat-

-------------- next part --------------

/**********************************************
 *
 * V4Ltheora.c by Mattia Roccoberton
 * ( based on encoder_example.c of libtheora )
 *
 **********************************************/

#include <fcntl.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <linux/videodev.h>

#include "theora/theora.h"


#define SPIN            "|\\-/"

#define V4L_DEVICE      "/dev/video0"

#define FRAME_WIDTH     352
#define FRAME_HEIGHT    288

#define TOT_FRAMES      50


int  V4L_Init     ( const char *szDevice );
void V4L_GetFrame ( void );
void V4L_Uninit   ( void );

int OGG_Init      ( void );
int OGG_OpenClip  ( const char *szFilename );
int OGG_CloseClip ( void );
int OGG_PutFrame  ( void );
int OGG_Uninit    ( void );


/* --- V4L --------------------------------------------------------------- */
int                 iV4Lfd;
int                 iV4LbufferSize;
unsigned char*      pucV4Lbuffer;
struct video_mmap   tV4LvideoMMap;

int V4L_Init( const char *szDevice )
{
    struct video_capability tVideoCapability;
    struct video_channel    tVideoChannel;
    struct video_mbuf       tVideoMBuf;
    int                     iRet = 0;

    printf( "+++ V4L_Init() \n" );
    if( szDevice == NULL )
    {
        fprintf( stderr, "### Invalid device name \n" );
        iRet = -1;
        goto V4L_Init_quit;
    }
    if( ( iV4Lfd = open( szDevice, O_RDWR ) ) == -1 )
    {
        perror( "### open()" );
        iRet = -2;
        goto V4L_Init_quit;
    }
    memset( &tVideoCapability, 0, sizeof( tVideoCapability ) );
    if( ioctl( iV4Lfd, VIDIOCGCAP, &tVideoCapability ) == -1 )
    {
        perror( "### ioctl() - VIDIOCGCAP" );
        iRet = -3;
        goto V4L_Init_quit;
    }
    memset( &tVideoChannel, 0, sizeof( tVideoChannel ) );
    tVideoChannel.channel = 0;
    if( ioctl( iV4Lfd, VIDIOCGCHAN, &tVideoChannel ) == -1 )
    {
        perror( "### ioctl() - VIDOCGCHAN" );
        iRet = -4;
        goto V4L_Init_quit;
    }
    tVideoChannel.norm = VIDEO_MODE_PAL;
    if( ioctl( iV4Lfd, VIDIOCSCHAN, &tVideoChannel ) == -1 )
    {
        perror( "### ioctl() - VIDIOCSCHAN" );
        iRet = -5;
        goto V4L_Init_quit;
    }
    tV4LvideoMMap.format  = VIDEO_PALETTE_YUV420P;
    tV4LvideoMMap.frame   = 0;
    tV4LvideoMMap.width   = FRAME_WIDTH;
    tV4LvideoMMap.height  = FRAME_HEIGHT;
    memset( &tVideoMBuf, 0, sizeof( tVideoMBuf ) );
    if( ioctl( iV4Lfd, VIDIOCGMBUF, &tVideoMBuf ) == -1 )
    {
        perror( "### ioctl() - VIDIOCGMBUF" );
        iRet = -6;
        goto V4L_Init_quit;
    }
    iV4LbufferSize = tVideoMBuf.size;
    pucV4Lbuffer = mmap( 0, iV4LbufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, iV4Lfd, 0 );
    if( ( pucV4Lbuffer == NULL ) || ( -1 == (int) pucV4Lbuffer ) )
    {
        fprintf( stderr, "### mmap() error \n" );
        iRet = -7;
        goto V4L_Init_quit;
    }

V4L_Init_quit:
    if( iRet < -2 )
        close( iV4Lfd );

    return iRet;
}

void V4L_GetFrame( void )
{
    fd_set   tFDset;
    int      i = 0;

    FD_ZERO( &tFDset );
    FD_SET( 0, &tFDset );
    FD_SET( iV4Lfd, &tFDset );
    select( iV4Lfd + 1, &tFDset, NULL, NULL, NULL ); 
    if( ioctl( iV4Lfd, VIDIOCMCAPTURE, &tV4LvideoMMap ) == -1 )
    {
        perror( "### ioctl() - VIDIOCMCAPTURE" );
        return;
    }
    if( ioctl( iV4Lfd, VIDIOCSYNC, &i ) == -1 )
    {
        perror( "### ioctrl() - VIDIOCSYNC" );
        return;
    }
}

void V4L_Uninit( void )
{
    printf( "+++ V4L_Uninit() \n" );
    if( pucV4Lbuffer != NULL )
    {
        munmap( pucV4Lbuffer, iV4LbufferSize );
        pucV4Lbuffer = NULL;
    }
    if( iV4Lfd != -1 )
    {
        close( iV4Lfd );
        iV4Lfd = -1;
    }
}


/* --- Ogg --------------------------------------------------------------- */
int iVideoX        = 0;
int iVideoY        = 0;
int iFrameOffsetX  = 0;
int iFrameOffsetY  = 0;
int iVideoFPSnum   = 25;   /* FPS 12.5 */
int iVideoFPSden   = 2;
int iVideoAspNum   = 1;
int iVideoAspDen   = 1;
int iVideoBitrate  = 400000;
int iVideoQuality  = 15;
int iFrameCounter;

unsigned char*      ucYUVframe = NULL;

FILE*               pfOGGclip = NULL;

ogg_stream_state    tOGGstreamState;
ogg_page            tOGGpage;
ogg_packet          tOGGpacket;

theora_state        tTheoraState;
theora_info         tTheoraInfo;
theora_comment      tTheoraComment;

yuv_buffer          tYUVbuffer;

int OGG_Init( void )
{
    printf( "+++ OGG_Init() \n" );
    srand( time( NULL ) );

/* Set up Theora encoder */

/* Theora has a divisible-by-sixteen restriction for the encoded video size */
/* scale the frame size up to the nearest /16 and calculate offsets */
    iVideoX = ( ( FRAME_WIDTH  + 15 ) >> 4 ) << 4;
    iVideoY = ( ( FRAME_HEIGHT + 15 ) >> 4 ) << 4;

/* We force the offset to be even. This ensures that the chroma samples align
   properly with the luma samples. */
    iFrameOffsetX = ( ( iVideoX - FRAME_WIDTH  ) / 2 ) & ~1;
    iFrameOffsetY = ( ( iVideoY - FRAME_HEIGHT ) / 2 ) & ~1;

    ucYUVframe = malloc( iVideoX * iVideoY * 3 / 2 );
    if( ucYUVframe == NULL )
    {
        fprintf( stderr, "### malloc() error \n" );

        return -1;
    }

    tYUVbuffer.y_width  = iVideoX;
    tYUVbuffer.y_height = iVideoY;
    tYUVbuffer.y_stride = iVideoX;

    tYUVbuffer.uv_width  = iVideoX / 2;
    tYUVbuffer.uv_height = iVideoY / 2;
    tYUVbuffer.uv_stride = iVideoX / 2;

    tYUVbuffer.y = ucYUVframe;
    tYUVbuffer.u = ucYUVframe + iVideoX * iVideoY;
    tYUVbuffer.v = ucYUVframe + iVideoX * iVideoY * 5 / 4;

    return 0;
}

int OGG_OpenClip( const char *szFilename )
{
    int iRet = 0;

    printf( "+++ OGG_OpenClip() \n" );

    theora_info_init( &tTheoraInfo );

    tTheoraInfo.width                = iVideoX;
    tTheoraInfo.height               = iVideoY;
    tTheoraInfo.frame_width          = FRAME_WIDTH;
    tTheoraInfo.frame_height         = FRAME_HEIGHT;
    tTheoraInfo.offset_x             = iFrameOffsetX;
    tTheoraInfo.offset_y             = iFrameOffsetY;

    tTheoraInfo.fps_numerator        = iVideoFPSnum;
    tTheoraInfo.fps_denominator      = iVideoFPSden;
    tTheoraInfo.aspect_numerator     = iVideoAspNum;
    tTheoraInfo.aspect_denominator   = iVideoAspDen;
    tTheoraInfo.colorspace           = OC_CS_UNSPECIFIED;
    tTheoraInfo.pixelformat          = OC_PF_420;
    tTheoraInfo.target_bitrate       = iVideoBitrate;
    tTheoraInfo.quality              = iVideoQuality;

    tTheoraInfo.dropframes_p                 = 0;
    tTheoraInfo.quick_p                      = 1;
    tTheoraInfo.keyframe_auto_p              = 1;
    tTheoraInfo.keyframe_frequency           = 64;
    tTheoraInfo.keyframe_frequency_force     = 64;
    tTheoraInfo.keyframe_data_target_bitrate = iVideoBitrate * 1.5;
    tTheoraInfo.keyframe_auto_threshold      = 80;
    tTheoraInfo.keyframe_mindistance         = 8;
    tTheoraInfo.noise_sensitivity            = 1;

    printf( "  %dx%d %dx%d %dx%d \n", tTheoraInfo.width, tTheoraInfo.height, tTheoraInfo.frame_width, tTheoraInfo.frame_height, tTheoraInfo.offset_x, tTheoraInfo.offset_y );
    printf( "  %d_%d %d_%d %d %d %d %d \n", tTheoraInfo.fps_numerator, tTheoraInfo.fps_denominator, tTheoraInfo.aspect_numerator, tTheoraInfo.aspect_denominator, tTheoraInfo.colorspace, tTheoraInfo.pixelformat, tTheoraInfo.target_bitrate, tTheoraInfo.quality );
    printf( "  %d %d %d %d_%d %d %d %d %d \n", tTheoraInfo.dropframes_p, tTheoraInfo.quick_p, tTheoraInfo.keyframe_auto_p, tTheoraInfo.keyframe_frequency, tTheoraInfo.keyframe_frequency_force, tTheoraInfo.keyframe_data_target_bitrate, tTheoraInfo.keyframe_auto_threshold, tTheoraInfo.keyframe_mindistance, tTheoraInfo.noise_sensitivity );

    pfOGGclip = fopen( szFilename, "wb" );
    if( pfOGGclip == NULL )
    {
        perror( "### fopen()" );
        iRet = -1;
        goto OGG_OpenClip_quit;
    }

    ogg_stream_init(  &tOGGstreamState, rand() );    /* need a random number */
    theora_encode_init( &tTheoraState, &tTheoraInfo );
    theora_info_clear( &tTheoraInfo );

/* first packet will get its own page automatically */
    theora_encode_header( &tTheoraState, &tOGGpacket );
    ogg_stream_packetin( &tOGGstreamState, &tOGGpacket );
    if( ogg_stream_pageout( &tOGGstreamState, &tOGGpage ) != 1 )
    {
        fprintf( stderr, "### Internal Ogg library error \n" );
        iRet = -2;
        goto OGG_OpenClip_quit;
    }
    fwrite( tOGGpage.header, 1, tOGGpage.header_len, pfOGGclip );
    fwrite( tOGGpage.body,   1, tOGGpage.body_len,   pfOGGclip );

/* create the remaining theora headers */
    theora_comment_init( &tTheoraComment );
    theora_encode_comment( &tTheoraComment, &tOGGpacket );
    ogg_stream_packetin( &tOGGstreamState, &tOGGpacket );

    _ogg_free( tOGGpacket.packet );    /* !!! correct ? small mem leak without this !!! */

    theora_encode_tables( &tTheoraState, &tOGGpacket );
    ogg_stream_packetin( &tOGGstreamState, &tOGGpacket );

/*
   Flush the rest of our headers. This ensures the actual data in each stream 
   will start on a new page, as per spec.
*/
    while( 1 )
    {
        int result = ogg_stream_flush( &tOGGstreamState, &tOGGpage );

        if( result < 0 )
        {
            fprintf( stderr, "### Internal Ogg library error {2} \n" );
            iRet = -3;
            goto OGG_OpenClip_quit;
        }
        if( result == 0 )
            break;

        fwrite( tOGGpage.header, 1, tOGGpage.header_len, pfOGGclip );
        fwrite( tOGGpage.body,   1, tOGGpage.body_len,   pfOGGclip );
    }

OGG_OpenClip_quit:
    if( iRet < -1 )
        fclose( pfOGGclip );

    return iRet;
}

int OGG_CloseClip( void )
{
    printf( "+++ OGG_CloseClip() \n" );

    ogg_stream_clear( &tOGGstreamState );
    theora_clear( &tTheoraState );
    if( pfOGGclip != NULL )
    {
        fclose( pfOGGclip );
        pfOGGclip = NULL;
    }

    return 0;
}

int OGG_PutFrame( void )
{
    ogg_page    tOGGpageTmp;
    ogg_packet  tOGGpacketTmp;

    memcpy( ucYUVframe, pucV4Lbuffer, iVideoX * iVideoY * 3 / 2 );
    theora_encode_YUVin( &tTheoraState, &tYUVbuffer );
    theora_encode_packetout( &tTheoraState, ( iFrameCounter == 0 ) ? 1 : 0, &tOGGpacketTmp );    /* last frame */
    ogg_stream_packetin( &tOGGstreamState, &tOGGpacketTmp );
    if( ogg_stream_pageout( &tOGGstreamState, &tOGGpageTmp ) > 0 )
    {
        fwrite( tOGGpageTmp.header, 1, tOGGpageTmp.header_len, pfOGGclip );
        fwrite( tOGGpageTmp.body,   1, tOGGpageTmp.body_len,   pfOGGclip );
        if( iFrameCounter == 0 )
            return 0;
    }
    if( ogg_stream_eos( &tOGGstreamState ) )
        return 0;

    return 1;
}

int OGG_Uninit( void )
{
    printf( "+++ OGG_Uninit() \n" );
    if( ucYUVframe != NULL )
    {
        free( ucYUVframe );
        ucYUVframe = NULL;
    }

    return 0;
}


/* --- main -------------------------------------------------------------- */

int main( int argc,char *argv[] )
{
    if( V4L_Init( V4L_DEVICE ) < 0 )
        return 1;
    if( OGG_Init() < 0 )
        return 2;
/* First clip */
    if( OGG_OpenClip( "test1.ogg" ) )
        return 3;
    iFrameCounter = TOT_FRAMES;
    do
    {
        fprintf( stderr, "\r%c", SPIN[iFrameCounter%4] );
        V4L_GetFrame();
        OGG_PutFrame();
    }
    while( iFrameCounter-- );
    printf( "\n" );
    OGG_CloseClip();
/* Second clip */
    if( OGG_OpenClip( "test2.ogg" ) )
        return 4;
    iFrameCounter = TOT_FRAMES;
    do
    {
        fprintf( stderr, "\r%c", SPIN[iFrameCounter%4] );
        V4L_GetFrame();
        OGG_PutFrame();
    }
    while( iFrameCounter-- );
    printf( "\n" );
    OGG_CloseClip();
/* Closing... */
    OGG_Uninit();
    V4L_Uninit();

    return 0;
}


More information about the theora-dev mailing list