优秀的编程知识分享平台

网站首页 > 技术文章 正文

【AWTK使用经验】如何播放视频或摄像头画面

nanyue 2024-08-17 19:08:25 技术文章 26 ℃

AWTK是基于C语言开发的跨平台GUI框架。《AWTK使用经验》系列文章将介绍开发AWTK过程中一些常见问题与解决方案,例如:如何加载外部资源?如何设计自定义进度条?这些都会在系列文章进行解答。

假设目前想在 ZTP800示教器 的AWTK程序中实现播放视频或者播放摄像头画面的效果,可以借助AWTK的mutable_image控件或usb_camera控件来显示画面。下图则是ZTP800示教器使用usb_camera控件Demo显示摄像头画面的效果图:

图 1 ZTP800示教器显示摄像头画面效果图

usb_camera控件和mutable_image控件的对比

关于文章开头提到的两个控件对比如下:

1. mutable_image控件

mutable_image控件可以显示视频画面,也可以显示摄像头等外接设备画面。适用性比较广泛,但使用起来相对繁琐一些。它本身是不负责解析视频或摄像头设备画面数据的,用户可以通过第三方库解析并获取画面原始RGB数据,将数据拷贝到mutable_image控件特定回调函数里面即可显示画面。

2. usb_camera控件

usb_camera控件本质上是对mutable_image控件的封装,专门用于显示摄像头画面。在控件内部实现了获取并解析摄像头画面数据的代码,用户只需调用特定的API即可显示摄像头画面,而不用手动拷贝原始RGB数据。与mutable_image相比没那么广,但是使用起来相对方便。

使用usb_camera控件显示摄像头画面

使用usb_camera控件显示摄像头画面的流程比较简单:在AWStudio插件列表可以安装该插件,之后在自己的AWTK应用放置一个usb_camera控件和一个按钮控件,设置usb_camera控件的摄像头设备、大小等属性,并且给该按钮注册点击事件用于打开摄像头。

图 2 使用usb_camera控件页面布局示例

按钮点击事件回调函数可以参考以下代码示例:

static ret_t on_button_click(void* ctx, event_t* e) {
  ret_t ret = RET_OK;
  widget_t* win = WIDGET(ctx);
  widget_t* camera = widget_lookup(win, "usb_camera", TRUE);


  if(usb_camera_is_open(camera) && usb_camera_is_play(camera)) {
    usb_camera_stop(camera);
    usb_camera_close(camera);
  } else {
    ret = usb_camera_open(camera);
    if (ret != RET_OK) {
      dialog_toast("open camera fail", 3000);
      return ret;
    }
    return usb_camera_play(camera);
  }
  return RET_OK;
}

使用mutable_image控件显示视频或摄像头画面

mutable_image控件主要提供了 mutable_image_set_prepare_image 函数注册一个回调函数,该回调函数在每次绘制之前被调用,用于准备下一帧要显示的图片。

由于mutable_image控件本身不带有获取视频或摄像头画面的功能,因此获取画面数据的操作可以借助第三方库(如:OpenCV)来实现。

mutable_image为了以最高性能绘制,会采取和当前LCD相同的位图数据格式(调用 lcd_get_desired_bitmap_format 获取)。因此为了兼容不同LCD格式,可以在回调函数中写好不同格式的拷贝像素逻辑。

下面则是利用OpenCV接口获取摄像头数据并显示到mutable_image控件的部分代码示例:

static cv::Mat cv_image;
static cv::VideoCapture cv_capture;


/* mutable_image_set_prepare_image回调函数 */
ret_t mutable_image_prepare_image(void* ctx, bitmap_t* image) {
  uint8_t* image_buff = NULL;
  uint32_t bpp = bitmap_get_bpp(image);
  widget_t* widget = WIDGET(ctx);
  bitmap_format_t format = (bitmap_format_t)image->format;
  
  cv_capture >> cv_image;
  
  image_buff = bitmap_lock_buffer_for_write(image);
  for (int h = 0; h < image->h; ++h) {
    uint8_t* p_buff = image_buff + image->w * h * bpp;
    for (int w = 0; w < image->w; ++w) {
      /* 调用OpenCV接口获取摄像头当前帧的原始RGB数据 */
      cv::Vec3b val = cv_image.at<cv::Vec3b>(h, w);
      if (format == BITMAP_FMT_RGB888 || format == BITMAP_FMT_RGBA8888) {
        /* RGB888或 RGBA8888示例,
        由于该示例上面cv::Mat::at数据的格式为BGR,需要进行转换,
        具体是否需要转换根据实际格式而决定 */
        uint8_t blue = val[0];
        uint8_t green = val[1];
        uint8_t red = val[2];
      
        p_buff[0] = red;
        p_buff[1] = green;
        p_buff[2] = blue;
        if (format == BITMAP_FMT_RGBA8888) {
          p_buff[3] = 255;
        }
      
        p_buff += bpp;


      } else if (format == BITMAP_FMT_BGR888 || format == BITMAP_FMT_BGRA8888) {
      ...
      } else if (format == BITMAP_FMT_BGR565) {
              
      } else if (format == BITMAP_FMT_RGB565) {
      ...
      }
    }
  }


  return bitmap_unlock_buffer(image);
}


ret_t camera_open(widget_t* win) {
  widget_t* mutable_image = widget_child(win, "mutable_image");


  cv_capture.open(0);
  mutable_image_set_prepare_image(mutable_image,mutable_image_prepare_image, mutable_image);
  return RET_OK;
}

上面代码通过设置mutable_image控件的回调函数,在回调函数里面使用OpenCV的API获取到摄像头原始RGB数据,最后将数据写到mutable_image控件画面缓冲区上。

最近发表
标签列表