kopia lustrzana https://github.com/F5OEO/rpidatv
Add new source mode: major update
rodzic
2c22665b10
commit
bc3cf2d3b3
|
@ -5,9 +5,9 @@ CFLAGS = -std=c++11 -Wno-multichar -Wall -Wno-format -g -I/opt/vc/include/IL -I/
|
|||
|
||||
LDFLAGS = -Xlinker -R/opt/vc/lib -L/opt/vc/lib/ -Xlinker -L/usr/local/lib -Xlinker -R/usr/local/lib # -Xlinker --verbose
|
||||
#LIBS = -L/opt/vc/lib -rdynamic -lopenmaxil -lvcos -lbcm_host -lpthread -L/libmpegts/libmpegts.o -lm
|
||||
LIBS = -lopenmaxil -lbcm_host -lvcos -lpthread -lm
|
||||
LIBS = -lopenmaxil -lbcm_host -lvcos -lpthread -lm -lrt -lvncclient
|
||||
|
||||
OFILES = avc2ts.o ./libmpegts/libmpegts.a
|
||||
OFILES = vncclient.o grabdisplay.o avc2ts.o webcam.o ./libmpegts/libmpegts.a
|
||||
|
||||
.PHONY: all clean install dist
|
||||
|
||||
|
|
|
@ -36,7 +36,20 @@ extern "C" {
|
|||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <fcntl.h> /* low-level i/o */
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "webcam.h"
|
||||
#include "grabdisplay.h"
|
||||
#include "vncclient.h"
|
||||
|
||||
//#include <linux/videodev2.h>
|
||||
|
||||
#define PROGRAM_VERSION "1.0.0"
|
||||
|
||||
|
@ -245,7 +258,8 @@ namespace broadcom
|
|||
VIDEO_ENCODER = 200,
|
||||
EGL_RENDER = 220,
|
||||
NULL_SINK = 240,
|
||||
VIDEO_SPLITTER = 250
|
||||
VIDEO_SPLITTER = 250,
|
||||
IMAGE_ENCODE = 340
|
||||
} ComponentType;
|
||||
|
||||
static const char * componentType2name(ComponentType type)
|
||||
|
@ -263,6 +277,7 @@ namespace broadcom
|
|||
case EGL_RENDER: return "OMX.broadcom.egl_render";
|
||||
case NULL_SINK: return "OMX.broadcom.null_sink";
|
||||
case VIDEO_SPLITTER: return "OMX.broadcom.video_splitter";
|
||||
case IMAGE_ENCODE: return "OMX.broadcom.image_encode";
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
@ -283,6 +298,7 @@ namespace broadcom
|
|||
case EGL_RENDER: return 2;
|
||||
case NULL_SINK: return 3;
|
||||
case VIDEO_SPLITTER: return 5;
|
||||
case IMAGE_ENCODE: return 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1420,20 +1436,22 @@ So the advice was for MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS and cir_mbs set prob
|
|||
{
|
||||
}
|
||||
|
||||
void setupOutputPort(const VideoFromat Videoformat,OMX_COLOR_FORMATTYPE colortype)
|
||||
void setupOutputPort(int SrcImageWidth,int SrcImageHeight,const VideoFromat Videoformat,OMX_COLOR_FORMATTYPE colortype)
|
||||
{
|
||||
//http://www.jvcref.com/files/PI/documentation/ilcomponents/resize.html
|
||||
Parameter<OMX_PARAM_PORTDEFINITIONTYPE> portDefI;
|
||||
getPortDefinition(IPORT, portDefI);
|
||||
|
||||
portDefI->format.image.nFrameWidth = Videoformat.width;
|
||||
portDefI->format.image.nFrameHeight = Videoformat.height;
|
||||
portDefI->format.image.nFrameWidth = SrcImageWidth;
|
||||
portDefI->format.image.nFrameHeight = SrcImageHeight;
|
||||
portDefI->format.image.eCompressionFormat = OMX_IMAGE_CodingUnused;
|
||||
portDefI->format.image.bFlagErrorConcealment = OMX_TRUE;
|
||||
//portDefI->format.image.bFlagErrorConcealment = OMX_TRUE;
|
||||
if(colortype==OMX_COLOR_FormatYUV420PackedPlanar)
|
||||
{
|
||||
portDefI->format.image.nStride = Videoformat.width;
|
||||
portDefI->format.image.nSliceHeight=Videoformat.height;
|
||||
|
||||
portDefI->format.image.nStride = SrcImageWidth;
|
||||
portDefI->format.image.nSliceHeight=SrcImageHeight;
|
||||
printf("%d * %d\n",portDefI->format.image.nStride,portDefI->format.image.nSliceHeight);
|
||||
}
|
||||
else
|
||||
{ portDefI->format.image.nStride =0 ;
|
||||
|
@ -1441,7 +1459,7 @@ So the advice was for MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS and cir_mbs set prob
|
|||
}
|
||||
|
||||
portDefI->format.image.eColorFormat=colortype;
|
||||
|
||||
|
||||
setPortDefinition(IPORT, portDefI);
|
||||
|
||||
// Output definition
|
||||
|
@ -1496,6 +1514,97 @@ So the advice was for MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS and cir_mbs set prob
|
|||
|
||||
};
|
||||
|
||||
|
||||
class ImageEncode : public Component
|
||||
{
|
||||
public:
|
||||
static const ComponentType cType = broadcom::IMAGE_ENCODE;
|
||||
|
||||
static const unsigned IPORT = 340;
|
||||
static const unsigned OPORT = 341;
|
||||
|
||||
ImageEncode()
|
||||
: Component(cType, (OMX_PTR) this, &cbsEvents)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void setupPort(int SrcImageWidth,int SrcImageHeight,OMX_COLOR_FORMATTYPE colortype)
|
||||
{
|
||||
//http://www.jvcref.com/files/PI/documentation/ilcomponents/resize.html
|
||||
Parameter<OMX_PARAM_PORTDEFINITIONTYPE> portDefI;
|
||||
getPortDefinition(IPORT, portDefI);
|
||||
|
||||
portDefI->format.image.nFrameWidth = SrcImageWidth;
|
||||
portDefI->format.image.nFrameHeight = SrcImageHeight;
|
||||
portDefI->format.image.eCompressionFormat = OMX_IMAGE_CodingUnused;
|
||||
//portDefI->format.image.bFlagErrorConcealment = OMX_TRUE;
|
||||
if(colortype==OMX_COLOR_FormatYUV420PackedPlanar)
|
||||
{
|
||||
|
||||
portDefI->format.image.nStride = SrcImageWidth;
|
||||
portDefI->format.image.nSliceHeight=SrcImageHeight;
|
||||
//printf("%d * %d\n",portDefI->format.image.nStride,portDefI->format.image.nSliceHeight);
|
||||
}
|
||||
else
|
||||
{ portDefI->format.image.nStride =SrcImageWidth ;
|
||||
portDefI->format.image.nSliceHeight=SrcImageHeight;
|
||||
}
|
||||
|
||||
portDefI->format.image.eColorFormat=colortype;
|
||||
|
||||
setPortDefinition(IPORT, portDefI);
|
||||
|
||||
// Output definition
|
||||
Parameter<OMX_PARAM_PORTDEFINITIONTYPE> portDefO;
|
||||
getPortDefinition(OPORT, portDefO);
|
||||
|
||||
portDefO->format.image.nFrameWidth = SrcImageWidth;
|
||||
portDefO->format.image.nFrameHeight = SrcImageHeight;
|
||||
portDefO->format.image.eCompressionFormat = OMX_IMAGE_CodingUnused;
|
||||
portDefO->format.image.nStride =SrcImageWidth;
|
||||
portDefO->format.image.nSliceHeight=SrcImageHeight;
|
||||
//portDefO->format.image.bFlagErrorConcealment = OMX_FALSE;
|
||||
portDefO->format.image.eColorFormat=OMX_COLOR_FormatYUV420PackedPlanar;//For encoder
|
||||
|
||||
|
||||
setPortDefinition(OPORT, portDefO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void allocBuffers()
|
||||
{
|
||||
|
||||
|
||||
Component::allocBuffers(IPORT, bufferIn_);
|
||||
|
||||
}
|
||||
|
||||
void freeBuffers()
|
||||
{
|
||||
|
||||
Component::freeBuffers(IPORT, bufferIn_);
|
||||
}
|
||||
|
||||
void callFillThisBuffer()
|
||||
{
|
||||
Component::callFillThisBuffer(bufferOut_);
|
||||
}
|
||||
|
||||
void callEmptyThisBuffer()
|
||||
{
|
||||
Component::callEmptyThisBuffer(bufferIn_);
|
||||
}
|
||||
|
||||
Buffer& outBuffer() { return bufferOut_; }
|
||||
Buffer& inBuffer() { return bufferIn_; }
|
||||
private:
|
||||
//Parameter<OMX_PARAM_PORTDEFINITIONTYPE> encoderPortDef_;
|
||||
Buffer bufferOut_;
|
||||
Buffer bufferIn_;
|
||||
|
||||
};
|
||||
//
|
||||
|
||||
static OMX_ERRORTYPE callback_EventHandler(
|
||||
|
@ -1585,6 +1694,13 @@ So the advice was for MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS and cir_mbs set prob
|
|||
Resizer * resizer = static_cast<Resizer *>(pAppData);
|
||||
resizer->inBuffer().setFilled();
|
||||
}
|
||||
|
||||
if (component->type() == ImageEncode::cType)
|
||||
{
|
||||
|
||||
ImageEncode * imageencode = static_cast<ImageEncode *>(pAppData);
|
||||
imageencode->inBuffer().setFilled();
|
||||
}
|
||||
return OMX_ErrorNone;
|
||||
}
|
||||
|
||||
|
@ -1648,6 +1764,7 @@ class TSEncaspulator
|
|||
|
||||
int m_sock;
|
||||
struct sockaddr_in m_client;
|
||||
|
||||
public:
|
||||
TSEncaspulator(){};
|
||||
void SetOutput(char *FileName,char *Udp)
|
||||
|
@ -1713,7 +1830,7 @@ class TSEncaspulator
|
|||
|
||||
}
|
||||
|
||||
void AddFrame(uint8_t *buffer,int size,int OmxFlags,uint64_t key_frame,int DelayPTS=200)
|
||||
void AddFrame(uint8_t *buffer,int size,int OmxFlags,uint64_t key_frame,int DelayPTS=200,struct timespec *Time=NULL)
|
||||
{
|
||||
//unsigned char buffer[100];
|
||||
ts_frame_t tsframe;
|
||||
|
@ -1763,6 +1880,7 @@ class TSEncaspulator
|
|||
TotalFrameSize+=tsframe.size;
|
||||
TimeToTransmitFrameUs= (TotalFrameSize*8.0*1000000.0/(float)MaxVideoBitrate);
|
||||
//if(OmxFlags&OMX_BUFFERFLAG_SYNCFRAME)
|
||||
if(Time==NULL)//Frame base calculation
|
||||
{
|
||||
//printf("IDR Image=%d TotalSize=%d Temps=%d\n",tsframe.size,TotalFrameSize,TimeToTransmitFrameUs);
|
||||
vdts=(key_frame*FrameDuration)*90L ; //TimeToTransmitFrameUs*90L/1000;
|
||||
|
@ -1775,7 +1893,18 @@ class TSEncaspulator
|
|||
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
//printf("%d:%d \n",Time->tv_sec,Time->tv_nsec);;
|
||||
vdts=(Time->tv_sec*1000+Time->tv_nsec/1000000)*90L ; //TimeToTransmitFrameUs*90L/1000;
|
||||
vpts=(Time->tv_sec*1000+Time->tv_nsec/1000000)*90L;
|
||||
|
||||
//tsframe.cpb_initial_arrival_time = vdts*300L - DelayPTS*90*300L ;
|
||||
//tsframe.cpb_final_arrival_time = vdts*300L - DelayPTS*90*300L ;
|
||||
tsframe.cpb_initial_arrival_time = vdts*300L - TimeToTransmitFrameUs*2.7- DelayPTS*90*300L ;
|
||||
tsframe.cpb_final_arrival_time = vdts*300L - TimeToTransmitFrameUs*2.7- DelayPTS*90*300L ;
|
||||
|
||||
}
|
||||
tsframe.dts = vdts;
|
||||
tsframe.pts = vpts;
|
||||
tsframe.random_access = key_frame;
|
||||
|
@ -1872,6 +2001,7 @@ private:
|
|||
uint64_t key_frame=1;
|
||||
VideoFromat CurrentVideoFormat;
|
||||
int DelayPTS;
|
||||
struct timespec InitTime;
|
||||
public:
|
||||
void Init(VideoFromat &VideoFormat,char *FileName,char *Udp,int VideoBitrate,int TsBitrate,int SetDelayPts,int VPid=256,int fps=25,int IDRPeriod=100,int RowBySlice=0,int EnableMotionVectors=0)
|
||||
{
|
||||
|
@ -1976,6 +2106,7 @@ ERR_OMX( OMX_SetupTunnel(camera.component(), Camera::OPORT_VIDEO, encoder.compon
|
|||
Buffer& encBuffer = encoder.outBuffer();
|
||||
if(FirstTime)
|
||||
{
|
||||
clock_gettime(CLOCK_REALTIME, &InitTime);
|
||||
FirstTime=false;
|
||||
encoder.callFillThisBuffer();
|
||||
}
|
||||
|
@ -2015,7 +2146,13 @@ ERR_OMX( OMX_SetupTunnel(camera.component(), Camera::OPORT_VIDEO, encoder.compon
|
|||
int OmxFlags=encBuffer.flags();
|
||||
if((OmxFlags&OMX_BUFFERFLAG_ENDOFFRAME)&&!(OmxFlags&OMX_BUFFERFLAG_CODECCONFIG))
|
||||
key_frame++;
|
||||
tsencoder.AddFrame(encBuffer.data(),encBuffer.dataSize(),OmxFlags,key_frame,DelayPTS);
|
||||
struct timespec gettime_now;
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
||||
gettime_now.tv_sec=(int)difftime(gettime_now.tv_sec,InitTime.tv_sec);
|
||||
tsencoder.AddFrame(encBuffer.data(),encBuffer.dataSize(),OmxFlags,key_frame,DelayPTS,&gettime_now);
|
||||
|
||||
//tsencoder.AddFrame(encBuffer.data(),encBuffer.dataSize(),OmxFlags,key_frame,DelayPTS);
|
||||
|
||||
|
||||
|
||||
|
@ -2093,16 +2230,28 @@ private:
|
|||
Encoder encoder;
|
||||
TSEncaspulator tsencoder;
|
||||
Resizer resizer;
|
||||
//ImageEncode colorconverter;
|
||||
int EncVideoBitrate;
|
||||
bool FirstTime=true;
|
||||
uint64_t key_frame=1;
|
||||
uint key_frame=1;
|
||||
VideoFromat CurrentVideoFormat;
|
||||
int DelayPTS;
|
||||
int Videofps;
|
||||
struct timespec last_time;
|
||||
int Mode=Mode_PATTERN;
|
||||
Webcam *pwebcam;
|
||||
GrabDisplay *pgrabdisplay;
|
||||
VncClient *pvncclient;
|
||||
struct timespec InitTime;
|
||||
|
||||
|
||||
public:
|
||||
static const int Mode_PATTERN=0;
|
||||
static const int Mode_V4L2=1;
|
||||
static const int Mode_GRABDISPLAY=2;
|
||||
static const int Mode_VNCCLIENT=3;
|
||||
public:
|
||||
void Init(VideoFromat &VideoFormat,char *FileName,char *Udp,int VideoBitrate,int TsBitrate,int SetDelayPts,int VPid=256,int fps=25,int IDRPeriod=100,int RowBySlice=0,int EnableMotionVectors=0)
|
||||
void Init(VideoFromat &VideoFormat,char *FileName,char *Udp,int VideoBitrate,int TsBitrate,int SetDelayPts,int VPid=256,int fps=25,int IDRPeriod=100,int RowBySlice=0,int EnableMotionVectors=0,int ModeInput=Mode_PATTERN,char *Extra=NULL)
|
||||
{
|
||||
last_time.tv_sec=0;
|
||||
last_time.tv_nsec=0;
|
||||
|
@ -2110,12 +2259,44 @@ public:
|
|||
Videofps=fps;
|
||||
DelayPTS=SetDelayPts;
|
||||
EncVideoBitrate=VideoBitrate;
|
||||
Mode=ModeInput;
|
||||
|
||||
// resizer.setupOutputPort(VideoFormat,OMX_COLOR_FormatYUV420PackedPlanar);
|
||||
resizer.setupOutputPort(VideoFormat,OMX_COLOR_Format32bitABGR8888);//OK
|
||||
resizer.setupOutputPort(VideoFormat,OMX_COLOR_FormatYCbYCr);
|
||||
// configuring encoders
|
||||
if(Mode==Mode_V4L2)
|
||||
{
|
||||
pwebcam=new Webcam(Extra);
|
||||
int CamWidth,CamHeight;
|
||||
pwebcam->GetCameraSize(CamWidth,CamHeight);
|
||||
printf("Resizer input = %d x %d\n",CamWidth,CamHeight);
|
||||
|
||||
resizer.setupOutputPort(CamWidth,CamHeight,VideoFormat,OMX_COLOR_FormatYUV420PackedPlanar);
|
||||
}
|
||||
if(Mode==Mode_PATTERN)
|
||||
{
|
||||
resizer.setupOutputPort(VideoFormat.width,VideoFormat.height,VideoFormat,OMX_COLOR_FormatYUV420PackedPlanar);
|
||||
}
|
||||
if(Mode==Mode_GRABDISPLAY)
|
||||
{
|
||||
pgrabdisplay=new GrabDisplay(0);
|
||||
int DisplayWidth,DisplayHeight,Rotate;
|
||||
|
||||
pgrabdisplay->GetDisplaySize(DisplayWidth,DisplayHeight,Rotate);
|
||||
printf("Resizer input = %d x %d\n",DisplayWidth,DisplayHeight);
|
||||
resizer.setupOutputPort(DisplayWidth,DisplayHeight,VideoFormat, OMX_COLOR_Format32bitABGR8888);
|
||||
}
|
||||
if(Mode==Mode_VNCCLIENT)
|
||||
{
|
||||
printf("Connecting to VNCSERVER %s...\n",Extra);
|
||||
pvncclient=new VncClient(Extra,"datv");
|
||||
int DisplayWidth,DisplayHeight,Rotate;
|
||||
|
||||
pvncclient->GetDisplaySize(DisplayWidth,DisplayHeight,Rotate);
|
||||
printf("Resizer input = %d x %d\n",DisplayWidth,DisplayHeight);
|
||||
resizer.setupOutputPort(DisplayWidth,DisplayHeight,VideoFormat, OMX_COLOR_Format32bitABGR8888);
|
||||
}
|
||||
//resizer.setupOutputPort(VideoFormat,OMX_COLOR_Format32bitABGR8888);//OK
|
||||
|
||||
// configuring encoders
|
||||
|
||||
VideoFromat vfResized = VideoFormat;
|
||||
|
||||
|
||||
|
@ -2148,12 +2329,15 @@ public:
|
|||
tsencoder.SetOutput(FileName,Udp);
|
||||
tsencoder.ConstructTsTree(VideoBitrate,TsBitrate,256,fps);
|
||||
printf("Ts bitrate = %d\n",TsBitrate);
|
||||
}
|
||||
|
||||
|
||||
|
||||
ERR_OMX( OMX_SetupTunnel(resizer.component(), Resizer::OPORT, encoder.component(), Encoder::IPORT), "tunnel resizer.output -> encoder.input (low)");
|
||||
|
||||
|
||||
// switch components to idle state
|
||||
{
|
||||
|
||||
resizer.switchState(OMX_StateIdle);
|
||||
encoder.switchState(OMX_StateIdle);
|
||||
|
||||
|
@ -2161,20 +2345,35 @@ public:
|
|||
|
||||
// enable ports
|
||||
{
|
||||
|
||||
resizer.enablePort();
|
||||
encoder.enablePort(); // all
|
||||
}
|
||||
|
||||
// allocate buffers
|
||||
{
|
||||
|
||||
|
||||
resizer.allocBuffers();
|
||||
printf("Allocsize= %d\n",resizer.inBuffer().allocSize());
|
||||
|
||||
if(Mode==Mode_V4L2)
|
||||
{
|
||||
pwebcam->SetOmxBuffer((unsigned char*)resizer.inBuffer().data());
|
||||
}
|
||||
if(Mode==Mode_GRABDISPLAY)
|
||||
{
|
||||
pgrabdisplay->SetOmxBuffer((unsigned char*)resizer.inBuffer().data());
|
||||
}
|
||||
if(Mode==Mode_VNCCLIENT)
|
||||
{
|
||||
pvncclient->SetOmxBuffer((unsigned char*)resizer.inBuffer().data());
|
||||
}
|
||||
printf("Allocsize= %d\n",resizer.inBuffer().allocSize());
|
||||
encoder.allocBuffers(false);//Only Bufout
|
||||
}
|
||||
|
||||
|
||||
// switch state of the components prior to starting
|
||||
{
|
||||
|
||||
resizer.switchState(OMX_StateExecuting);
|
||||
encoder.switchState(OMX_StateExecuting);
|
||||
}
|
||||
|
@ -2183,7 +2382,8 @@ public:
|
|||
|
||||
void usleep_exactly(long MuToSleep )
|
||||
{
|
||||
#define KERNEL_GRANULARITY 80000
|
||||
#define KERNEL_GRANULARITY 1000000
|
||||
#define MARGIN 500
|
||||
struct timespec gettime_now;
|
||||
long time_difference;
|
||||
if(last_time.tv_sec==0) clock_gettime(CLOCK_REALTIME, &last_time);
|
||||
|
@ -2205,8 +2405,9 @@ void usleep_exactly(long MuToSleep )
|
|||
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
||||
time_difference = gettime_now.tv_nsec - last_time.tv_nsec;
|
||||
if(time_difference<0) time_difference+=1E9;
|
||||
//printf("#");
|
||||
}
|
||||
while(time_difference<MuToSleep*1000L);
|
||||
while(time_difference<(MuToSleep*1000L-MARGIN));
|
||||
//printf("TimeDiff =%ld\n",time_difference);
|
||||
last_time=gettime_now;
|
||||
|
||||
|
@ -2256,6 +2457,43 @@ void usleep_exactly(long MuToSleep )
|
|||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
int ConvertColor(OMX_U8 *out,OMX_U8 *in,int Size)
|
||||
{
|
||||
OMX_U8 *inprocess=in;
|
||||
int Width=(fmt.fmt.pix.width>>5)<<5;
|
||||
int WidthMissing=fmt.fmt.pix.width-((fmt.fmt.pix.width>>5)<<5);
|
||||
int Height=(fmt.fmt.pix.height>>4)<<4;
|
||||
OMX_U8 *PlanY=out;
|
||||
OMX_U8 *PlanU=out+Width*Height;
|
||||
OMX_U8 *PlanV=PlanU+(Width*Height)/4;
|
||||
|
||||
//printf("WidthMissin %d\n",WidthMissing);
|
||||
int count=0;
|
||||
for(int j=0;j<Height;j++)
|
||||
{
|
||||
for(int i=0;i<Width/2;i++)
|
||||
{
|
||||
|
||||
*(PlanU)=*(inprocess++);
|
||||
if((j%2==0)) PlanU++;
|
||||
*(PlanY++)=*(inprocess++);
|
||||
*(PlanV)=*(inprocess++);
|
||||
if((j%2==0)) PlanV++;
|
||||
*(PlanY++)=*(inprocess++);
|
||||
|
||||
}
|
||||
count+=4*Width/2;
|
||||
if(count>Size) return 0;
|
||||
inprocess+=WidthMissing*2;
|
||||
count+=WidthMissing*2;
|
||||
}
|
||||
//printf("Count =%d\n",count);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
void Run(bool want_quit)
|
||||
{
|
||||
Buffer& encBuffer = encoder.outBuffer();
|
||||
|
@ -2267,8 +2505,50 @@ void Run(bool want_quit)
|
|||
|
||||
OMX_U32 filledLen;
|
||||
// generate_test_card(PictureBuffer.data(),&filledLen,key_frame);
|
||||
generate_test_rgbcard(PictureBuffer.data(),&filledLen,key_frame);
|
||||
if(Mode==Mode_PATTERN)
|
||||
{
|
||||
generate_test_card(PictureBuffer.data(),&filledLen,key_frame);
|
||||
|
||||
usleep_exactly(1e6/Videofps);
|
||||
}
|
||||
if(Mode==Mode_V4L2)
|
||||
{
|
||||
|
||||
auto frame = pwebcam->frame(2);
|
||||
|
||||
filledLen=frame.size;
|
||||
|
||||
//V4L2_read_frame(PictureBuffer.data(),&filledLen);
|
||||
//usleep_exactly(1e6/(Videofps*2));
|
||||
}
|
||||
if(Mode==Mode_GRABDISPLAY)
|
||||
{
|
||||
|
||||
pgrabdisplay->GetPicture();
|
||||
int DisplayWidth,DisplayHeight,Rotate;
|
||||
|
||||
pgrabdisplay->GetDisplaySize(DisplayWidth,DisplayHeight,Rotate);
|
||||
filledLen=DisplayWidth*DisplayHeight*4;
|
||||
//printf("%d filled\n",filledLen);
|
||||
usleep_exactly(1e6/Videofps);
|
||||
}
|
||||
|
||||
if(Mode==Mode_VNCCLIENT)
|
||||
{
|
||||
|
||||
int FrameDiff=pvncclient->GetPicture(Videofps);
|
||||
|
||||
filledLen=PictureBuffer.allocSize();
|
||||
//printf("%d filled\n",filledLen);
|
||||
if(FrameDiff==0)
|
||||
usleep_exactly(1e6/Videofps);
|
||||
else
|
||||
{
|
||||
//usleep_exactly((FrameDiff+1)*1e6/Videofps);
|
||||
key_frame+=FrameDiff;
|
||||
//printf("DiffFrame %d\n",FrameDiff);
|
||||
}
|
||||
}
|
||||
PictureBuffer.setDatasize(filledLen);
|
||||
PictureBuffer.setFilled(false);
|
||||
resizer.callEmptyThisBuffer();
|
||||
|
@ -2276,8 +2556,10 @@ void Run(bool want_quit)
|
|||
|
||||
if(FirstTime)
|
||||
{
|
||||
encoder.callFillThisBuffer();
|
||||
clock_gettime(CLOCK_REALTIME, &InitTime);
|
||||
FirstTime=false;
|
||||
encoder.callFillThisBuffer();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2316,22 +2598,36 @@ void Run(bool want_quit)
|
|||
|
||||
int OmxFlags=encBuffer.flags();
|
||||
if((OmxFlags&OMX_BUFFERFLAG_ENDOFFRAME)&&!(OmxFlags&OMX_BUFFERFLAG_CODECCONFIG))
|
||||
{
|
||||
key_frame++;
|
||||
tsencoder.AddFrame(encBuffer.data(),encBuffer.dataSize(),OmxFlags,key_frame,DelayPTS);
|
||||
|
||||
struct timespec gettime_now;
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
||||
gettime_now.tv_sec=(int)difftime(gettime_now.tv_sec,InitTime.tv_sec);
|
||||
tsencoder.AddFrame(encBuffer.data(),encBuffer.dataSize(),OmxFlags,key_frame,DelayPTS,&gettime_now);
|
||||
|
||||
|
||||
//tsencoder.AddFrame(encBuffer.data(),encBuffer.dataSize(),OmxFlags,key_frame,DelayPTS);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//usleep_exactly(1e6/(2*Videofps));
|
||||
key_frame++; //Skipped Frame, key_frame++ to allow correct timing for next valid frames
|
||||
printf("! -------------------------------------------------->\n ");
|
||||
printf("!%ld\n",key_frame);
|
||||
}
|
||||
|
||||
// Buffer flushed, request a new buffer to be filled by the encoder component
|
||||
encBuffer.setFilled(false);
|
||||
//PictureBuffer.setFilled(true);
|
||||
encoder.callFillThisBuffer();
|
||||
PictureBuffer.setFilled(true);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2340,6 +2636,18 @@ void Run(bool want_quit)
|
|||
void Terminate()
|
||||
{
|
||||
|
||||
if(Mode==Mode_V4L2)
|
||||
{
|
||||
free(pwebcam);
|
||||
}
|
||||
if(Mode==Mode_GRABDISPLAY)
|
||||
{
|
||||
free(pgrabdisplay);
|
||||
}
|
||||
if(Mode==Mode_VNCCLIENT)
|
||||
{
|
||||
free(pvncclient);
|
||||
}
|
||||
// return the last full buffer back to the encoder component
|
||||
{
|
||||
encoder.outBuffer().flags() &= OMX_BUFFERFLAG_EOS;
|
||||
|
@ -2350,33 +2658,35 @@ void Run(bool want_quit)
|
|||
|
||||
// flush the buffers on each component
|
||||
{
|
||||
|
||||
resizer.flushPort();
|
||||
encoder.flushPort();
|
||||
}
|
||||
|
||||
// disable all the ports
|
||||
{
|
||||
|
||||
|
||||
resizer.disablePort();
|
||||
encoder.disablePort();
|
||||
}
|
||||
|
||||
// free all the buffers
|
||||
{
|
||||
|
||||
|
||||
resizer.freeBuffers();
|
||||
encoder.freeBuffers();
|
||||
}
|
||||
|
||||
// transition all the components to idle states
|
||||
{
|
||||
|
||||
resizer.switchState(OMX_StateIdle);
|
||||
encoder.switchState(OMX_StateIdle);
|
||||
}
|
||||
|
||||
// transition all the components to loaded states
|
||||
{
|
||||
|
||||
|
||||
resizer.switchState(OMX_StateLoaded);
|
||||
encoder.switchState(OMX_StateLoaded);
|
||||
}
|
||||
|
@ -2401,6 +2711,10 @@ Usage:\nrpi-avc2ts -o OutputFile -b BitrateVideo -m BitrateMux -x VideoWidth -
|
|||
-d Delay PTS/PCR in ms\n\
|
||||
-v Enable Motion vectors\n\
|
||||
-i IDR Period\n\
|
||||
-t TypeInput {0=Picamera,1=InternalPatern,2=USB Camera,3=Rpi Display,4=VNC}\n\
|
||||
-e Extra Arg:\n\
|
||||
- For usb camera name of device (/dev/video0)\n\
|
||||
- For VNC : IP address of VNC Server. Password must be datv\n\
|
||||
-h help (print this help).\n\
|
||||
Example : ./rpi-avc2ts -o result.ts -b 1000000 -m 1400000 -x 640 -y 480 -f 25 -n 230.0.0.1:1000\n\
|
||||
\n",\
|
||||
|
@ -2426,13 +2740,17 @@ int main(int argc, char **argv)
|
|||
int RowBySlice=0;
|
||||
char *NetworkOutput=NULL;//"230.0.0.1:10000";
|
||||
int EnableMotionVectors=0;
|
||||
char *ExtraArg=NULL;
|
||||
#define CAMERA 0
|
||||
#define PATTERN 1
|
||||
#define USB_CAMERA 2
|
||||
#define DISPLAY 3
|
||||
#define VNC 4
|
||||
int TypeInput=CAMERA;
|
||||
|
||||
while(1)
|
||||
{
|
||||
a = getopt(argc, argv, "o:b:m:hx:y:f:n:d:i:r:vt:");
|
||||
a = getopt(argc, argv, "o:b:m:hx:y:f:n:d:i:r:vt:e:");
|
||||
|
||||
if(a == -1)
|
||||
{
|
||||
|
@ -2487,16 +2805,19 @@ int main(int argc, char **argv)
|
|||
case 't': //Type input
|
||||
TypeInput=atoi(optarg);
|
||||
break;
|
||||
case 'e': //Type input extra arg
|
||||
ExtraArg=optarg;
|
||||
break;
|
||||
case -1:
|
||||
break;
|
||||
case '?':
|
||||
if (isprint(optopt) )
|
||||
{
|
||||
fprintf(stderr, "Omxts: unknown option `-%c'.\n", optopt);
|
||||
fprintf(stderr, "avc2ts: unknown option `-%c'.\n", optopt);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Omxts: unknown option character `\\x%x'.\n", optopt);
|
||||
fprintf(stderr, "avc2ts: unknown option character `\\x%x'.\n", optopt);
|
||||
}
|
||||
print_usage();
|
||||
|
||||
|
@ -2538,13 +2859,29 @@ bcm_host_init();
|
|||
VcosSemaphore sem("common semaphore");
|
||||
pSemaphore = &sem;
|
||||
|
||||
CameraTots cameratots;
|
||||
PictureTots picturetots;
|
||||
CameraTots *cameratots;
|
||||
PictureTots *picturetots;
|
||||
if(TypeInput==0)
|
||||
cameratots.Init(CurrentVideoFormat,OutputFileName,NetworkOutput,VideoBitrate,MuxBitrate,DelayPTS,256,VideoFramerate,IDRPeriod,RowBySlice);
|
||||
{
|
||||
cameratots=new CameraTots;
|
||||
cameratots->Init(CurrentVideoFormat,OutputFileName,NetworkOutput,VideoBitrate,MuxBitrate,DelayPTS,256,VideoFramerate,IDRPeriod,RowBySlice,false);
|
||||
}
|
||||
else
|
||||
picturetots.Init(CurrentVideoFormat,OutputFileName,NetworkOutput,VideoBitrate,MuxBitrate,DelayPTS,256,VideoFramerate,IDRPeriod,RowBySlice);
|
||||
{
|
||||
int PictureMode=PictureTots::Mode_PATTERN;
|
||||
switch(TypeInput)
|
||||
{
|
||||
case PATTERN:PictureMode=PictureTots::Mode_PATTERN;break;
|
||||
case USB_CAMERA:PictureMode=PictureTots::Mode_V4L2;
|
||||
if(ExtraArg==NULL) ExtraArg="/dev/video0";break;
|
||||
case DISPLAY:PictureMode=PictureTots::Mode_GRABDISPLAY;break;
|
||||
case VNC:PictureMode=PictureTots::Mode_VNCCLIENT;
|
||||
if(ExtraArg==NULL) {printf("IP of VNCServer should be set with -e option\n");exit(0);}
|
||||
|
||||
}
|
||||
picturetots = new PictureTots;
|
||||
picturetots->Init(CurrentVideoFormat,OutputFileName,NetworkOutput,VideoBitrate,MuxBitrate,DelayPTS,256,VideoFramerate,IDRPeriod,RowBySlice,false,PictureMode,ExtraArg);
|
||||
}
|
||||
#if 1
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
|
@ -2567,9 +2904,9 @@ else
|
|||
{
|
||||
|
||||
if(TypeInput==0)
|
||||
cameratots.Run(want_quit);
|
||||
cameratots->Run(want_quit);
|
||||
else
|
||||
picturetots.Run(want_quit);
|
||||
picturetots->Run(want_quit);
|
||||
|
||||
|
||||
|
||||
|
@ -2604,9 +2941,15 @@ else
|
|||
signal(SIGQUIT, SIG_DFL);
|
||||
#endif
|
||||
if(TypeInput==0)
|
||||
cameratots.Terminate();
|
||||
{
|
||||
cameratots->Terminate();
|
||||
delete(cameratots);
|
||||
}
|
||||
else
|
||||
picturetots.Terminate();
|
||||
{
|
||||
picturetots->Terminate();
|
||||
delete(picturetots);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
|
||||
|
||||
#include <getopt.h>
|
||||
#include <math.h>
|
||||
#include <png.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "grabdisplay.h"
|
||||
|
||||
GrabDisplay::GrabDisplay(int displayNumber)
|
||||
{
|
||||
//bcm_host_init();
|
||||
displayHandle=vc_dispmanx_display_open(displayNumber);
|
||||
|
||||
if (displayHandle == 0)
|
||||
{
|
||||
fprintf(stderr,
|
||||
" unable to open display %d\n",
|
||||
|
||||
displayNumber);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
DISPMANX_MODEINFO_T modeInfo;
|
||||
result = vc_dispmanx_display_get_info(displayHandle, &modeInfo);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
fprintf(stderr, " unable to get display information\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
DisplayWidth = (modeInfo.width>>5)<<5;
|
||||
DisplayHeight = (modeInfo.height>>4)<<4;
|
||||
dmxPitch=DisplayWidth*4; //4 BYTES IN RGBA //2 BYTE PAR PIXEL IN RGB565
|
||||
resourceHandle = vc_dispmanx_resource_create(imageType,
|
||||
DisplayWidth,
|
||||
DisplayHeight,
|
||||
&vcImagePtr);
|
||||
}
|
||||
GrabDisplay::~GrabDisplay()
|
||||
{
|
||||
vc_dispmanx_resource_delete(resourceHandle);
|
||||
vc_dispmanx_display_close(displayHandle);
|
||||
}
|
||||
|
||||
void GrabDisplay::GetDisplaySize(int& Width,int& Height,int& Rotate)
|
||||
{
|
||||
Width=DisplayWidth;
|
||||
Height=DisplayHeight;
|
||||
Rotate=0;
|
||||
}
|
||||
|
||||
void GrabDisplay::SetOmxBuffer(unsigned char* Buffer)
|
||||
{
|
||||
OmxBuffer=Buffer;
|
||||
}
|
||||
|
||||
void GrabDisplay::GetPicture()
|
||||
{
|
||||
// FOR YUV420
|
||||
//https://www.raspberrypi.org/forums/viewtopic.php?t=45711&p=481480
|
||||
//https://github.com/raspberrypi/firmware/issues/235
|
||||
int result = vc_dispmanx_snapshot(displayHandle,
|
||||
resourceHandle,
|
||||
DISPMANX_NO_ROTATE);
|
||||
if (result != 0)
|
||||
{
|
||||
vc_dispmanx_resource_delete(resourceHandle);
|
||||
vc_dispmanx_display_close(displayHandle);
|
||||
|
||||
fprintf(stderr, " vc_dispmanx_snapshot() failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
VC_RECT_T rect;
|
||||
result = vc_dispmanx_rect_set(&rect, 0, 0, DisplayWidth, DisplayHeight);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
vc_dispmanx_resource_delete(resourceHandle);
|
||||
vc_dispmanx_display_close(displayHandle);
|
||||
|
||||
fprintf(stderr, " vc_dispmanx_rect_set() failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
result = vc_dispmanx_resource_read_data(resourceHandle,
|
||||
&rect,
|
||||
OmxBuffer,
|
||||
dmxPitch);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
#include "bcm_host.h"
|
||||
|
||||
class GrabDisplay {
|
||||
|
||||
public:
|
||||
GrabDisplay(int displayNumber);
|
||||
|
||||
~GrabDisplay();
|
||||
void GetDisplaySize(int& Width,int& Height,int& Rotate);
|
||||
void SetOmxBuffer(unsigned char* Buffer);
|
||||
void GetPicture();
|
||||
|
||||
private:
|
||||
VC_IMAGE_TYPE_T imageType = VC_IMAGE_RGBA32;//VC_IMAGE_YUV420;//VC_IMAGE_RGBA32;//VC_IMAGE_RGB565;
|
||||
DISPMANX_DISPLAY_HANDLE_T displayHandle;
|
||||
DISPMANX_RESOURCE_HANDLE_T resourceHandle;
|
||||
uint32_t vcImagePtr = 0;
|
||||
int32_t DisplayWidth;
|
||||
int32_t DisplayHeight ;
|
||||
int32_t dmxPitch;
|
||||
unsigned char *OmxBuffer=0;
|
||||
};
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
|
||||
|
||||
#include <getopt.h>
|
||||
#include <math.h>
|
||||
#include <png.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "vncclient.h"
|
||||
|
||||
char* MyPassword;
|
||||
|
||||
//http://svn.openscenegraph.org/osg/OpenSceneGraph/trunk/src/osgPlugins/vnc/ReaderWriterVNC.cpp
|
||||
rfbBool VncClient::resize(rfbClient* client) {
|
||||
|
||||
|
||||
/* VncClient::DisplayWidth = (client->width>>5)<<5;
|
||||
VncClient::DisplayHeight = (client->height>>4)<<4;
|
||||
*/
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
void VncClient::update(rfbClient* client,int x,int y,int w,int h)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
char* VncClient::GetPassword(rfbClient* client)
|
||||
{
|
||||
char* Password=(char*)malloc(255);
|
||||
strcpy(Password,MyPassword);
|
||||
return Password;
|
||||
}
|
||||
|
||||
VncClient::VncClient(char *IP,char* Password)
|
||||
{
|
||||
client = rfbGetClient(8,3,4);
|
||||
DisplayWidth=0;
|
||||
DisplayHeight=0;
|
||||
//client->MallocFrameBuffer=&VncClient::resize;
|
||||
client->GotFrameBufferUpdate=update;
|
||||
|
||||
client->GetPassword=GetPassword;
|
||||
MyPassword=Password;
|
||||
char *argv[2];
|
||||
argv[0]="rpidatv";
|
||||
argv[1]=IP;
|
||||
int argc=2;
|
||||
if(!rfbInitClient(client,&argc,argv))
|
||||
{
|
||||
printf("InitClient Failed\n");
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
VncClient::~VncClient()
|
||||
{
|
||||
printf("VNC Clean todo\n");
|
||||
//rfbClientCleanup(client);
|
||||
}
|
||||
|
||||
void VncClient::GetDisplaySize(int& Width,int& Height,int& Rotate)
|
||||
{
|
||||
//printf("Enter DisplaySize\n");
|
||||
while(DisplayWidth==0)
|
||||
{ sleep(1);
|
||||
if(!WaitForMessage(client,50000)) continue;
|
||||
if(!HandleRFBServerMessage(client)) printf("Handle Error");
|
||||
|
||||
if((client->width!=0)&&(client->height!=0))
|
||||
{
|
||||
DisplayWidth = ((client->width>>5))<<5;
|
||||
DisplayHeight = ((client->height>>4)+1)<<4;
|
||||
client->width=DisplayWidth;
|
||||
client->height=DisplayHeight;
|
||||
}
|
||||
}
|
||||
printf("Origin FB = %d * %d\n",client->width,client->height);
|
||||
Width=DisplayWidth;
|
||||
Height=DisplayHeight;
|
||||
}
|
||||
|
||||
void VncClient::SetOmxBuffer(unsigned char* Buffer)
|
||||
{
|
||||
client->frameBuffer=Buffer;
|
||||
}
|
||||
|
||||
int VncClient::GetPicture(int fps=25)
|
||||
{
|
||||
int result;
|
||||
struct timespec gettime_now,last_time;
|
||||
long time_difference;
|
||||
|
||||
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &last_time);
|
||||
|
||||
|
||||
result=WaitForMessage(client,500000);
|
||||
if(result<0) {printf("Message Failed\n");return 0;}
|
||||
if(result==0)
|
||||
{
|
||||
return 0;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!HandleRFBServerMessage(client)) printf("VNC KAPUT\n");
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
||||
time_difference = gettime_now.tv_nsec - last_time.tv_nsec;
|
||||
if(time_difference<0) time_difference+=1E9;
|
||||
int FrameDiff=(int)((time_difference/1000)/(1000000/fps));
|
||||
//printf("Diff=%ld us Frame %d\n",time_difference/1000,FrameDiff);
|
||||
//int FrameDiff=0;
|
||||
return FrameDiff;
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
extern "C"
|
||||
{
|
||||
#include <rfb/rfbclient.h>
|
||||
}
|
||||
|
||||
class VncClient {
|
||||
|
||||
public:
|
||||
VncClient(char *IP,char* Password);
|
||||
|
||||
~VncClient();
|
||||
void GetDisplaySize(int& Width,int& Height,int& Rotate);
|
||||
void SetOmxBuffer(unsigned char* Buffer);
|
||||
int GetPicture(int fps);
|
||||
|
||||
|
||||
private:
|
||||
int32_t DisplayWidth=0;
|
||||
int32_t DisplayHeight=0;
|
||||
|
||||
static rfbBool resize(rfbClient* client);
|
||||
static void update(rfbClient* client,int x,int y,int w,int h) ;
|
||||
static char* GetPassword(rfbClient* client);
|
||||
rfbClient* client;
|
||||
unsigned char *OmxBuffer=0;
|
||||
};
|
||||
|
|
@ -0,0 +1,462 @@
|
|||
/*
|
||||
|
||||
(c) 2014 Séverin Lemaignan <severin.lemaignan@epfl.ch>
|
||||
(c) 2008 Hans de Goede <hdegoede@redhat.com> for yuyv_to_rgb24
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
|
||||
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h> /* low-level i/o */
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h> // strerrno
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
|
||||
#include "webcam.h"
|
||||
|
||||
#define CLEAR(x) memset(&(x), 0, sizeof(x))
|
||||
|
||||
using namespace std;
|
||||
|
||||
static int xioctl(int fh, unsigned long int request, void *arg)
|
||||
{
|
||||
int r;
|
||||
|
||||
do {
|
||||
r = ioctl(fh, request, arg);
|
||||
} while (-1 == r && EINTR == errno);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*****
|
||||
* Taken from libv4l2 (in v4l-utils)
|
||||
*
|
||||
* (C) 2008 Hans de Goede <hdegoede@redhat.com>
|
||||
*
|
||||
* Released under LGPL
|
||||
*/
|
||||
/*
|
||||
#define CLIP(color) (unsigned char)(((color) > 0xFF) ? 0xff : (((color) < 0) ? 0 : (color)))
|
||||
|
||||
static void v4lconvert_yuyv_to_rgb24(const unsigned char *src,
|
||||
unsigned char *dest,
|
||||
int width, int height,
|
||||
int stride)
|
||||
{
|
||||
int j;
|
||||
|
||||
while (--height >= 0) {
|
||||
for (j = 0; j + 1 < width; j += 2) {
|
||||
int u = src[1];
|
||||
int v = src[3];
|
||||
int u1 = (((u - 128) << 7) + (u - 128)) >> 6;
|
||||
int rg = (((u - 128) << 1) + (u - 128) +
|
||||
((v - 128) << 2) + ((v - 128) << 1)) >> 3;
|
||||
int v1 = (((v - 128) << 1) + (v - 128)) >> 1;
|
||||
|
||||
*dest++ = CLIP(src[0] + v1);
|
||||
*dest++ = CLIP(src[0] - rg);
|
||||
*dest++ = CLIP(src[0] + u1);
|
||||
|
||||
*dest++ = CLIP(src[2] + v1);
|
||||
*dest++ = CLIP(src[2] - rg);
|
||||
*dest++ = CLIP(src[2] + u1);
|
||||
src += 4;
|
||||
}
|
||||
src += stride - (width * 2);
|
||||
}
|
||||
}
|
||||
*/
|
||||
int Webcam::ConvertColor(unsigned char *out,unsigned char *in)
|
||||
{
|
||||
|
||||
unsigned char *inprocess=in;
|
||||
|
||||
int WidthMissing=fmt.fmt.pix.width-yuv420frame.width;
|
||||
|
||||
unsigned char *PlanY=out;
|
||||
unsigned char *PlanU=out+yuv420frame.width*yuv420frame.height;
|
||||
unsigned char *PlanV=PlanU+(yuv420frame.width*yuv420frame.height)/4;
|
||||
|
||||
//printf("WidthMissin %d\n",WidthMissing);
|
||||
int count=0;
|
||||
for(int j=0;j<yuv420frame.height;j++)
|
||||
{
|
||||
for(int i=0;i<yuv420frame.width/2;i++)
|
||||
{
|
||||
|
||||
*(PlanU)=*(inprocess++);
|
||||
if((j%2==0)) PlanU++;
|
||||
*(PlanY++)=*(inprocess++);
|
||||
*(PlanV)=*(inprocess++);
|
||||
if((j%2==0)) PlanV++;
|
||||
*(PlanY++)=*(inprocess++);
|
||||
|
||||
}
|
||||
|
||||
|
||||
inprocess+=WidthMissing*2;
|
||||
count+=WidthMissing*2;
|
||||
}
|
||||
//printf("Count =%d\n",count);
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
|
||||
|
||||
Webcam::Webcam(const string& device) :
|
||||
device(device)
|
||||
{
|
||||
open_device();
|
||||
init_device();
|
||||
// xres and yres are set to the actual resolution provided by the cam
|
||||
|
||||
// frame stored as UYVY
|
||||
yuv420frame.width = (xres>>5)<<5; //32 pixels aligned
|
||||
yuv420frame.height = (yres>>4)<<4;//16 pixels aligned
|
||||
yuv420frame.size = (yuv420frame.width * yuv420frame.height * 3)/2;
|
||||
//yuv420frame.data = (unsigned char *) malloc(yuv420frame.size * sizeof(char));
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Webcam::GetCameraSize(int& Width,int& Height)
|
||||
{
|
||||
Width=yuv420frame.width;
|
||||
Height=yuv420frame.height;
|
||||
}
|
||||
|
||||
void Webcam::SetOmxBuffer(unsigned char* Buffer)
|
||||
{
|
||||
yuv420frame.data=Buffer;
|
||||
}
|
||||
Webcam::~Webcam()
|
||||
{
|
||||
stop_capturing();
|
||||
uninit_device();
|
||||
close_device();
|
||||
|
||||
//free(yuv420frame.data); Not its own buffer
|
||||
}
|
||||
|
||||
const YUV420Image& Webcam::frame(int timeout)
|
||||
{
|
||||
if(!StatusCapturing)
|
||||
{
|
||||
start_capturing();
|
||||
StatusCapturing=true;
|
||||
}
|
||||
for (;;) {
|
||||
fd_set fds;
|
||||
struct timeval tv;
|
||||
int r;
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fd, &fds);
|
||||
|
||||
|
||||
tv.tv_sec = timeout;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
r = select(fd + 1, &fds, NULL, NULL, &tv);
|
||||
|
||||
if (-1 == r) {
|
||||
if (EINTR == errno)
|
||||
continue;
|
||||
throw runtime_error("select");
|
||||
}
|
||||
|
||||
if (0 == r) {
|
||||
printf("Timeout\n");
|
||||
throw runtime_error(device + ": select timeout");
|
||||
}
|
||||
|
||||
int idx = read_frame();
|
||||
if (idx != -1) {
|
||||
//ConvertColor(yuv420frame.data,(unsigned char *) buffers[idx].data);
|
||||
|
||||
return yuv420frame;
|
||||
}
|
||||
else
|
||||
printf("#");
|
||||
/* EAGAIN - continue select loop. */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool Webcam::read_frame()
|
||||
{
|
||||
|
||||
struct v4l2_buffer buf;
|
||||
unsigned int i;
|
||||
|
||||
CLEAR(buf);
|
||||
|
||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buf.memory = V4L2_MEMORY_MMAP;
|
||||
|
||||
if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
|
||||
switch (errno) {
|
||||
case EAGAIN:
|
||||
return -1;
|
||||
|
||||
case EIO:
|
||||
/* Could ignore EIO, see spec. */
|
||||
|
||||
/* fall through */
|
||||
|
||||
default:
|
||||
throw runtime_error("VIDIOC_DQBUF");
|
||||
}
|
||||
}
|
||||
|
||||
assert(buf.index < n_buffers);
|
||||
//printf("Image =%d/%d\n",buf.bytesused,buffers[buf.index].size);
|
||||
if(buf.bytesused==buffers[buf.index].size)
|
||||
{
|
||||
ConvertColor(yuv420frame.data,(unsigned char *) buffers[buf.index].data);
|
||||
}
|
||||
else
|
||||
{
|
||||
//ConvertColor(yuv420frame.data,(unsigned char *) buffers[buf.index].data);
|
||||
}
|
||||
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
|
||||
throw runtime_error("VIDIOC_QBUF");
|
||||
|
||||
return buf.index;
|
||||
}
|
||||
|
||||
void Webcam::open_device(void)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (-1 == stat(device.c_str(), &st)) {
|
||||
throw runtime_error(device + ": cannot identify! " + to_string(errno) + ": " + strerror(errno));
|
||||
}
|
||||
|
||||
if (!S_ISCHR(st.st_mode)) {
|
||||
throw runtime_error(device + " is no device");
|
||||
}
|
||||
|
||||
fd = open(device.c_str(), O_RDWR /* required */ | O_NONBLOCK, 0);
|
||||
|
||||
if (-1 == fd) {
|
||||
throw runtime_error(device + ": cannot open! " + to_string(errno) + ": " + strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Webcam::init_mmap(void)
|
||||
{
|
||||
struct v4l2_requestbuffers req;
|
||||
|
||||
CLEAR(req);
|
||||
|
||||
req.count = 4;
|
||||
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
req.memory = V4L2_MEMORY_MMAP;
|
||||
|
||||
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
|
||||
if (EINVAL == errno) {
|
||||
throw runtime_error(device + " does not support memory mapping");
|
||||
} else {
|
||||
throw runtime_error("VIDIOC_REQBUFS");
|
||||
}
|
||||
}
|
||||
printf("%d buffers for Video\n",req.count);
|
||||
if (req.count < 2) {
|
||||
throw runtime_error(string("Insufficient buffer memory on ") + device);
|
||||
}
|
||||
|
||||
buffers = (buffer*) calloc(req.count, sizeof(*buffers));
|
||||
|
||||
if (!buffers) {
|
||||
throw runtime_error("Out of memory");
|
||||
}
|
||||
|
||||
for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
|
||||
struct v4l2_buffer buf;
|
||||
|
||||
CLEAR(buf);
|
||||
|
||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buf.memory = V4L2_MEMORY_MMAP;
|
||||
buf.index = n_buffers;
|
||||
|
||||
if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf))
|
||||
throw runtime_error("VIDIOC_QUERYBUF");
|
||||
|
||||
buffers[n_buffers].size = buf.length;
|
||||
buffers[n_buffers].data =
|
||||
mmap(NULL /* start anywhere */,
|
||||
buf.length,
|
||||
PROT_READ | PROT_WRITE /* required */,
|
||||
MAP_SHARED /* recommended */,
|
||||
fd, buf.m.offset);
|
||||
|
||||
if (MAP_FAILED == buffers[n_buffers].data)
|
||||
throw runtime_error("mmap");
|
||||
}
|
||||
}
|
||||
|
||||
void Webcam::close_device(void)
|
||||
{
|
||||
if (-1 == close(fd))
|
||||
throw runtime_error("close");
|
||||
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
void Webcam::init_device(void)
|
||||
{
|
||||
struct v4l2_capability cap;
|
||||
struct v4l2_cropcap cropcap;
|
||||
struct v4l2_crop crop;
|
||||
//struct v4l2_format fmt;
|
||||
unsigned int min;
|
||||
|
||||
if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) {
|
||||
if (EINVAL == errno) {
|
||||
throw runtime_error(device + " is no V4L2 device");
|
||||
} else {
|
||||
throw runtime_error("VIDIOC_QUERYCAP");
|
||||
}
|
||||
}
|
||||
|
||||
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
|
||||
throw runtime_error(device + " is no video capture device");
|
||||
}
|
||||
|
||||
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
|
||||
throw runtime_error(device + " does not support streaming i/o");
|
||||
}
|
||||
|
||||
/* Select video input, video standard and tune here. */
|
||||
|
||||
|
||||
CLEAR(cropcap);
|
||||
|
||||
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
|
||||
if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {
|
||||
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
crop.c = cropcap.defrect; /* reset to default */
|
||||
|
||||
if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) {
|
||||
switch (errno) {
|
||||
case EINVAL:
|
||||
/* Cropping not supported. */
|
||||
break;
|
||||
default:
|
||||
/* Errors ignored. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Errors ignored. */
|
||||
}
|
||||
|
||||
|
||||
CLEAR(fmt);
|
||||
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
if (force_format) {
|
||||
fmt.fmt.pix.width = xres;
|
||||
fmt.fmt.pix.height = yres;
|
||||
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
|
||||
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
|
||||
|
||||
if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))
|
||||
throw runtime_error("VIDIOC_S_FMT");
|
||||
|
||||
if (fmt.fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV)
|
||||
// note that libv4l2 (look for 'v4l-utils') provides helpers
|
||||
// to manage conversions
|
||||
throw runtime_error("Webcam does not support YUYV format. Support for more format need to be added!");
|
||||
|
||||
/* Note VIDIOC_S_FMT may change width and height. */
|
||||
xres = fmt.fmt.pix.width;
|
||||
yres = fmt.fmt.pix.height;
|
||||
|
||||
stride = fmt.fmt.pix.bytesperline;
|
||||
|
||||
|
||||
} else {
|
||||
/* Preserve original settings as set by v4l2-ctl for example */
|
||||
if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt))
|
||||
throw runtime_error("VIDIOC_G_FMT");
|
||||
xres = fmt.fmt.pix.width;
|
||||
yres = fmt.fmt.pix.height;
|
||||
|
||||
}
|
||||
|
||||
init_mmap();
|
||||
}
|
||||
|
||||
|
||||
void Webcam::uninit_device(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < n_buffers; ++i)
|
||||
if (-1 == munmap(buffers[i].data, buffers[i].size))
|
||||
throw runtime_error("munmap");
|
||||
|
||||
free(buffers);
|
||||
}
|
||||
|
||||
void Webcam::start_capturing(void)
|
||||
{
|
||||
unsigned int i;
|
||||
enum v4l2_buf_type type;
|
||||
|
||||
for (i = 0; i < n_buffers; ++i) {
|
||||
struct v4l2_buffer buf;
|
||||
|
||||
CLEAR(buf);
|
||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buf.memory = V4L2_MEMORY_MMAP;
|
||||
buf.index = i;
|
||||
|
||||
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
|
||||
throw runtime_error("VIDIOC_QBUF");
|
||||
}
|
||||
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))
|
||||
throw runtime_error("VIDIOC_STREAMON");
|
||||
}
|
||||
|
||||
void Webcam::stop_capturing(void)
|
||||
{
|
||||
enum v4l2_buf_type type;
|
||||
|
||||
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type))
|
||||
throw runtime_error("VIDIOC_STREAMOFF");
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/** Small C++ wrapper around V4L example code to access the webcam
|
||||
**/
|
||||
|
||||
#include <string>
|
||||
#include <memory> // unique_ptr
|
||||
#include <linux/videodev2.h>
|
||||
struct buffer {
|
||||
void *data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct YUV420Image {
|
||||
unsigned char *data; // YUV420
|
||||
size_t width;
|
||||
size_t height;
|
||||
size_t size; // width * height * 3/2
|
||||
};
|
||||
|
||||
|
||||
class Webcam {
|
||||
|
||||
public:
|
||||
Webcam(const std::string& device = "/dev/video0");
|
||||
|
||||
~Webcam();
|
||||
|
||||
void GetCameraSize(int& Width,int& Height);
|
||||
int ConvertColor(unsigned char *out,unsigned char *in);
|
||||
void SetOmxBuffer(unsigned char* Buffer);
|
||||
/** Captures and returns a frame from the webcam.
|
||||
*
|
||||
* The returned object contains a field 'data' with the image data in YUV420
|
||||
* format
|
||||
* This call blocks until a frame is available or until the provided
|
||||
* timeout (in seconds).
|
||||
*
|
||||
* Throws a runtime_error if the timeout is reached.
|
||||
*/
|
||||
const YUV420Image& frame(int timeout = 1);
|
||||
|
||||
private:
|
||||
void init_mmap();
|
||||
|
||||
void open_device();
|
||||
void close_device();
|
||||
|
||||
void init_device();
|
||||
void uninit_device();
|
||||
|
||||
void start_capturing();
|
||||
void stop_capturing();
|
||||
|
||||
bool read_frame();
|
||||
|
||||
std::string device;
|
||||
int fd;
|
||||
|
||||
YUV420Image yuv420frame;
|
||||
struct buffer *buffers;
|
||||
unsigned int n_buffers;
|
||||
|
||||
size_t xres, yres;
|
||||
size_t stride;
|
||||
struct v4l2_format fmt;
|
||||
bool force_format = false;
|
||||
bool StatusCapturing=false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
Ładowanie…
Reference in New Issue