📄 video.cxx
字号:
{
return ReadSrcFrame(frameStores, buffer, width, height, amount);
}
BOOL MCUSimpleVideoMixer::WriteFrame(ConferenceMemberId id, const void * buffer, int width, int height, PINDEX amount)
{
PWaitAndSignal m(mutex);
// special case of one member means we do nothing, and don't bother looking for other user to copy from
if (rows == 0)
return TRUE;
// special case of two members means we do nothing, and tell caller to look for another frame to write to
if (rows == 1)
return FALSE;
// write data into sub frame of mixed image
VideoMixPositionMap::const_iterator r = videoPositions.find(id);
if (r == videoPositions.end())
return TRUE;
r->second->WriteSubFrame(*this, buffer, width, height, amount);
return TRUE;
}
void MCUSimpleVideoMixer::SetAudioLevel(ConferenceMemberId id, unsigned audioLevel)
{
PWaitAndSignal m(mutex);
// write data into sub frame of mixed image
VideoMixPositionMap::const_iterator r = videoPositions.find(id);
if (r != videoPositions.end()) {
r->second->audioLevel = audioLevel;
SetSubFrameLevel(*r->second, audioLevel);
}
}
void MCUSimpleVideoMixer::SetSubFrameLevel(VideoMixPosition & /*vmp*/, unsigned /*audioLevel*/)
{
}
void MCUSimpleVideoMixer::CalcVideoSplitSize(unsigned int imageCount, int & subImageWidth, int & subImageHeight, int & cols, int & rows)
{
if (!forceScreenSplit && imageCount < 2) {
subImageWidth = CIF_WIDTH;
subImageHeight = CIF_HEIGHT;
cols = 0;
rows = 0;
}
else
if (!forceScreenSplit && imageCount == 2) {
subImageWidth = CIF_WIDTH;
subImageHeight = CIF_HEIGHT;
cols = 1;
rows = 1;
}
else
if (imageCount <= 4) {
subImageWidth = QCIF_WIDTH;
subImageHeight = QCIF_HEIGHT;
cols = 2;
rows = 2;
}
else
if (imageCount <= 9) {
subImageWidth = Q3CIF_WIDTH;
subImageHeight = Q3CIF_HEIGHT;
cols = 3;
rows = 3;
}
else if (imageCount <= 16) {
subImageWidth = Q4CIF_WIDTH;
subImageHeight = Q4CIF_HEIGHT;
cols = 4;
rows = 4;
}
}
void MCUSimpleVideoMixer::ReallocatePositions()
{
FillCIFYUVFrame(frameStores.GetFrameStore(CIF_WIDTH, CIF_HEIGHT).data.GetPointer(), 0, 0, 0);
frameStores.InvalidateExcept(CIF_WIDTH, CIF_HEIGHT);
VideoMixPositionMap::iterator r;
if (cols == 0 || cols == 1) {
r = videoPositions.begin();
if (r != videoPositions.end()) {
VideoMixPosition & vmp = *(r->second);
vmp.xpos = 0;
vmp.ypos = 0;
vmp.width = subImageWidth;
vmp.height = subImageHeight;
}
return;
}
// reallocate display positions, but always use the top left corner last)
unsigned int start = 1;
unsigned int i = 0;
for (r = videoPositions.begin(); r != videoPositions.end(); ++r) {
unsigned int x = (i + start) % cols;
unsigned int y = ((i + start) / cols) % rows;
VideoMixPosition & vmp = *(r->second);
vmp.xpos = x * subImageWidth;
vmp.ypos = y * subImageHeight;
vmp.width = subImageWidth;
vmp.height = subImageHeight;
++i;
}
}
BOOL MCUSimpleVideoMixer::AddVideoSource(ConferenceMemberId id, ConferenceMember & mbr)
{
PWaitAndSignal m(mutex);
// make sure this source is not already in the list
VideoMixPositionMap::const_iterator r = videoPositions.find(id);
if (r != videoPositions.end())
return TRUE;
if (videoPositions.size() == MAX_SUBFRAMES)
return FALSE;
// calculate the kind of video split we need to include the new source
int newSubImageWidth, newSubImageHeight, newCols, newRows;
CalcVideoSplitSize((unsigned int)(videoPositions.size()+1), newSubImageWidth, newSubImageHeight, newCols, newRows);
VideoMixPosition * newPosition = NULL;
// if the subimage size has changed, rearrange everything
if ((newSubImageWidth != subImageWidth) || (newSubImageHeight != subImageHeight) || (cols != newCols) || (rows != newRows)) {
rows = newRows;
cols = newCols;
subImageWidth = newSubImageWidth;;
subImageHeight = newSubImageHeight;
newPosition = CreateVideoMixPosition(id, mbr, 0, 0, subImageWidth, subImageHeight);
videoPositions.insert(VideoMixPositionMap::value_type(id, newPosition));
ReallocatePositions();
}
// otherwise find an empty position
else if (rows == 0) {
newPosition = CreateVideoMixPosition(id, mbr, 0, 0,subImageWidth, subImageHeight);
videoPositions.insert(VideoMixPositionMap::value_type(id, newPosition));
}
else {
BOOL found = FALSE;
int i = 0;
int start = 1;
VideoMixPositionMap::iterator r = videoPositions.end();
while (!found && i < (rows*cols)) {
int x = (i + start) % cols;
int y = ((i + start) / cols) % rows;
r = videoPositions.begin();
while (r != videoPositions.end()) {
VideoMixPosition & vmp = *(r->second);
if (vmp.xpos == (x * subImageWidth) && vmp.ypos == (y * subImageHeight))
break;
++r;
}
if (r == videoPositions.end())
break;
++i;
}
PAssert(r == videoPositions.end(), "could not find free video position");
unsigned int x = (i + start) % cols;
unsigned int y = ((i + start) / cols) % rows;
newPosition = CreateVideoMixPosition(id, mbr, x * subImageWidth, y * subImageHeight, subImageWidth, subImageHeight);
videoPositions.insert(VideoMixPositionMap::value_type(id, newPosition));
}
if (newPosition != NULL) {
PBYTEArray fs(CIF_SIZE);
int amount = CIF_SIZE;
if (!OpenMCU::Current().GetEmptyMediaFrame(fs.GetPointer(), CIF_WIDTH, CIF_HEIGHT, amount))
FillYUVFrame(fs.GetPointer(), 0, 0, 0, CIF_WIDTH, CIF_HEIGHT);
WriteSubFrame(*newPosition, fs.GetPointer(), CIF_WIDTH, CIF_HEIGHT, amount);
}
return TRUE;
}
void MCUSimpleVideoMixer::RemoveVideoSource(ConferenceMemberId id, ConferenceMember & mbr)
{
PWaitAndSignal m(mutex);
// make sure this source is in the list
{
VideoMixPositionMap::iterator r = videoPositions.find(id);
if (r == videoPositions.end())
return;
// clear the position where the frame was
VideoMixPosition & vmp = *(r->second);
if (videoPositions.size() == 1)
FillCIFYUVFrame(frameStores.GetFrameStore(CIF_WIDTH, CIF_HEIGHT).data.GetPointer(), 0, 0, 0);
else
FillCIFYUVRect(frameStores.GetFrameStore(CIF_WIDTH, CIF_HEIGHT).data.GetPointer(), 0, 0, 0, vmp.xpos, vmp.ypos, vmp.width, vmp.height);
frameStores.InvalidateExcept(CIF_WIDTH, CIF_HEIGHT);
// erase the video position information
delete r->second;
// remove the source from the list
videoPositions.erase(r);
}
// calculate the kind of video split we need to include the new source
int newSubImageWidth, newSubImageHeight, newCols, newRows;
CalcVideoSplitSize((unsigned int)videoPositions.size(), newSubImageWidth, newSubImageHeight, newCols, newRows);
// if the subimage size has changed, rearrange everything
if (newSubImageWidth != subImageWidth || newSubImageHeight != subImageHeight || newRows != rows || newCols != cols) {
rows = newRows;
cols = newCols;
subImageWidth = newSubImageWidth;
subImageHeight = newSubImageHeight;
ReallocatePositions();
}
}
BOOL MCUSimpleVideoMixer::WriteSubFrame(VideoMixPosition & vmp, const void * buffer, int width, int height, PINDEX amount)
{
PWaitAndSignal m(mutex);
VideoFrameStoreList::FrameStore & cifFs = frameStores.GetFrameStore(CIF_WIDTH, CIF_HEIGHT);
if (width == vmp.width && height == vmp.height) {
CopyRectIntoCIF(buffer, cifFs.data.GetPointer(), vmp.xpos, vmp.ypos, vmp.width, vmp.height);
frameStores.InvalidateExcept(CIF_WIDTH, CIF_HEIGHT);
}
else {
converter->SetSrcFrameSize(width, height);
PINDEX bytesReturned = (vmp.width * vmp.height * 3) / 2;
converter->SetDstFrameSize(vmp.width, vmp.height, TRUE);
converter->Convert((const BYTE *)buffer, imageStore.GetPointer((vmp.width * vmp.height * 3) / 2), amount, &bytesReturned);
CopyRectIntoCIF(imageStore.GetPointer(), cifFs.data.GetPointer(), vmp.xpos, vmp.ypos, vmp.width, vmp.height);
frameStores.InvalidateExcept(CIF_WIDTH, CIF_HEIGHT);
}
return TRUE;
};
///////////////////////////////////////////////////////////////////////////////////////
#if ENABLE_TEST_ROOMS
TestVideoMixer::TestVideoMixer(unsigned _frames)
: frames(_frames), allocated(FALSE)
{
}
BOOL TestVideoMixer::AddVideoSource(ConferenceMemberId id, ConferenceMember & mbr)
{
PWaitAndSignal m(mutex);
if (allocated)
return FALSE;
allocated = TRUE;
unsigned i;
for (i = 0; i < frames; ++i) {
// calculate the kind of video split we need to include the new source
CalcVideoSplitSize(i+1, subImageWidth, subImageHeight, cols, rows);
VideoMixPosition * newPosition = CreateVideoMixPosition(id, mbr, 0, 0, subImageWidth, subImageHeight);
videoPositions.insert(VideoMixPositionMap::value_type((ConferenceMemberId)(i+(BYTE *)id), newPosition));
ReallocatePositions();
}
return TRUE;
}
BOOL TestVideoMixer::ReadFrame(ConferenceMember &, void * buffer, int width, int height, PINDEX & amount)
{
PWaitAndSignal m(mutex);
// not allocated means fill with black
if (!allocated) {
VideoFrameStoreList::FrameStore & fs = frameStores.GetFrameStore(width, height);
if (!fs.valid) {
if (!OpenMCU::Current().GetEmptyMediaFrame(fs.data.GetPointer(), width, height, amount))
FillYUVFrame(fs.data.GetPointer(), 0, 0, 0, width, height);
fs.valid = TRUE;
}
memcpy(buffer, fs.data.GetPointer(), amount);
return TRUE;
}
return ReadMixedFrame(buffer, width, height, amount);
}
BOOL TestVideoMixer::WriteFrame(ConferenceMemberId id, const void * buffer, int width, int height, PINDEX amount)
{
PWaitAndSignal m(mutex);
unsigned i;
for (i = 0; i < frames; ++i) {
// write data into sub frame of mixed image
VideoMixPositionMap::const_iterator r;
for (r = videoPositions.begin(); r != videoPositions.end(); ++r) {
r->second->WriteSubFrame(*this, buffer, width, height, amount);
}
}
return TRUE;
}
#endif // ENABLE_TEST_ROOMS
#if ENABLE_ECHO_MIXER
EchoVideoMixer::EchoVideoMixer()
: MCUSimpleVideoMixer()
{
}
BOOL EchoVideoMixer::AddVideoSource(ConferenceMemberId id, ConferenceMember & mbr)
{
PWaitAndSignal m(mutex);
CalcVideoSplitSize(1, subImageWidth, subImageHeight, cols, rows);
VideoMixPosition * newPosition = CreateVideoMixPosition(id, mbr, 0, 0, subImageWidth, subImageHeight);
videoPositions.insert(VideoMixPositionMap::value_type(id, newPosition));
ReallocatePositions();
return TRUE;
}
BOOL EchoVideoMixer::WriteFrame(ConferenceMemberId id, const void * buffer, int width, int height, PINDEX amount)
{
PWaitAndSignal m(mutex);
// write data into sub frame of mixed image
VideoMixPositionMap::const_iterator r = videoPositions.begin();
if (r != videoPositions.end())
r->second->WriteSubFrame(*this, buffer, width, height, amount);
return TRUE;
}
BOOL EchoVideoMixer::ReadFrame(ConferenceMember &, void * buffer, int width, int height, PINDEX & amount)
{
return ReadMixedFrame(buffer, width, height, amount);
}
#endif // ENABLE_ECHO_MIXER
#endif // OPENMCU_VIDEO
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -