[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