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