在视频监控或者视频会议中最常用的就是图像拼接和字符叠加,25FPS的视频流,如果每隔40MS就从各个通道中取一幅图像来合成,则可以看到一个实时的合成视频。合成的过程也就是原始图像的拼接、缩放的过程,本文主要阐述UV分开存储的YUV420图像拼接的过程,实现下图的效果。
图像原理
YUV色彩空间的重要性是它的亮度信号Y和色度信号U、V是分离的
YUV420=W*H*3/2 BYTE
YUV422=W*H*2 BYTE
RGB=W*H*3 BYTE
图像合成过程
分别把各路视频进行采样缩放、贴图后就能实现图像合成。
//获取每个区域的起始点与高宽
static int get_widget_info(ViewContext* p, ViewArea* s, int num)
{
switch (p->total)
{
case ONE_WND:
{
}break;
case TWO_WND:
{
}break;
case FOUR_WND:
{
s->w = p->w / 2;
s->h = p->h / 2;
s->x = (num % 2) * s->w;
s->y = (num / 2) * s->h;
}break;
case SIX_WND:
{
return get_widget_info_six(p, s, num);
}break;
case EIGHT_WND:
{
return get_widget_info_eight(p, s, num);
}break;
case NINE_WND:
{ s->w = p->w / 3;
s->h = p->h / 3;
s->x = (num % 3) * s->w;
s->y = (num / 3) * s->h;
}break;
case SIXTEEN_WND:
{
s->w = p->w / 4;
s->h = p->h / 4;
s->x = (num % 4) * s->w;
s->y = (num / 4) * s->h;
yuv_draw_rect(p->frame->data[0], p->frame->width, p->frame->height, p->frame->width, s->x, s->y, s->x+ s->w, s->y+s->h, makergba(0, 0, 0,0));
}break;
default:
{
break;
}
}
return -1;
}
在每个视频源加个矩形框:
void yuv_draw_rect(unsigned char* dst_buf, int w, int h, int lpitch, int x0, int y0, int x1, int y1, unsigned long rgba)
{
unsigned char r, g, b, y, cb, cr;
r = (rgba >> 24) & 255;
g = (rgba >> 16) & 255;
b = (rgba >> 8) & 255;
//a = (rgba) & 255;
//r=GetRValue(rgba);
//g = GetGValue(rgba);
//b = GetBValue(rgba);
//b = (rgba >> 8) & 0xFF;
//g = (rgba >> 16) & 0xFF;
//r = (rgba >> 24) & 0xFF;
y = 16 + 0.257 * r + 0.504 * g + 0.098 * b;
cb = 128 - 0.148 * r - 0.291 * g + 0.439 * b;
cr = 128 + 0.439 * r - 0.368 * g - 0.071 * b;
int rect_width, rect_height;
x0 = x0 & 0xFFFE;
y0 = y0 & 0xFFFE;
x1 = x1 & 0xFFFE;
y1 = y1 & 0xFFFE;
rect_width = x1 - x0;
rect_height = y1 - y0;
unsigned char* xoff;
unsigned char* yoff;
xoff = dst_buf + y0 * lpitch + x0;
memset(xoff, y, rect_width);//rowline
xoff = dst_buf + (y0 + 1) * lpitch + x0;
memset(xoff, y, rect_width);
xoff = dst_buf + (y1 - 1) * lpitch + x0;
memset(xoff, y, rect_width);//colline
xoff = dst_buf + y1 * lpitch + x0;
memset(xoff, y, rect_width);
//y first
for (int i = 0; i < rect_height; i++)
{
yoff = dst_buf + (i + y0) * lpitch + x0;
*yoff = y;
yoff = dst_buf + (i + y0) * lpitch + x0 + 1;
*yoff = y;
yoff = dst_buf + (i + y0) * lpitch + x1 - 1;
*yoff = y;
yoff = dst_buf + (i + y0) * lpitch + x1;
*yoff = y;
}
//cb next
xoff = dst_buf + lpitch * h + y0 * lpitch / 4 + x0 / 2;
memset(xoff, cb, rect_width / 2);
xoff = dst_buf + lpitch * h + y1 * lpitch / 4 + x0 / 2;
memset(xoff, cb, rect_width / 2);
for (int i = 0; i < rect_height; i += 2)
{
yoff = dst_buf + lpitch * h + (i + y0) * lpitch / 4 + x0 / 2;
*yoff = cb;
yoff = dst_buf + lpitch * h + (i + y0) * lpitch / 4 + x1 / 2;
*yoff = cb;
}
//cr last
xoff = dst_buf + lpitch * h + lpitch * h / 4 + y0 * lpitch / 4 + x0 / 2;
memset(xoff, cr, rect_width / 2);
xoff = dst_buf + lpitch * h + lpitch * h / 4 + y1 * lpitch / 4 + x0 / 2;
memset(xoff, cr, rect_width / 2);
for (int i = 0; i < rect_height; i += 2)
{
yoff = dst_buf + lpitch * h + lpitch * h / 4 + (i + y0) * lpitch / 4 + x0 / 2;
*yoff = cr;
yoff = dst_buf + lpitch * h + lpitch * h / 4 + (i + y0) * lpitch / 4 + x1 / 2;
*yoff = cr;
}
}
图像粘贴:
hi_avframe_t* yuv420_map_image(ViewContext* p, ViewArea* s, hi_avframe_t* src)
{
if (!s->frame)
{
s->frame = PlayMedia_New_Avframe(s->w * s->h * 3 / 2);
s->frame->width = s->w;
s->frame->height = s->h;
s->frame->data[0] = (uint8_t*)s->frame->data[0];
s->frame->linesize[0] = s->w;
s->frame->data[1] = s->frame->data[0] + s->frame->linesize[0] * s->frame->height;
s->frame->linesize[1] = s->w / 2;
s->frame->data[2] = s->frame->data[0] + s->frame->linesize[0] * s->frame->height * 5 / 4;
s->frame->linesize[2] = s->w / 2;
}
//图像缩放
if (libyuv::I420Scale(src->data[0], src->linesize[0],
src->data[1], src->linesize[1],
src->data[2], src->linesize[2],
src->width, src->height,
s->frame->data[0], s->frame->linesize[0],
s->frame->data[1], s->frame->linesize[1],
s->frame->data[2], s->frame->linesize[2],
s->frame->width, s->frame->height, libyuv::kFilterBox) == 0)
{
media_overlay_t overlay;
memset(&overlay, 0, sizeof(overlay));
overlay.x = s->x;
overlay.y = s->y;
overlay.w = s->w;
overlay.h = s->h;
overlay.alpha = 0;
overlay.src_alpha = 0;
overlay.mask = 0;
//yuv_blend(p->frame, frame, &overlay);
//视频粘贴
PlayMedia_YuvOverlay(p->frame, s->frame, &overlay);
}
}
作者:Aliveyun
版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。