视频流中YUV420图像拼接怎么实现

在视频监控或者视频会议中最常用的就是图像拼接和字符叠加,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 举报,一经查实,本站将立刻删除。

(1)

发表回复

登录后才能评论