我们在做RTSP、RTMP播放器的时候,遇到这样的诉求:特别是RTSP,有些摄像头安装可能倒置或者旋转了90°亦或270°,拉取到图像,势必需要对视频图像做一定的处理,确保显示正常。
为此,我们提供了以下接口:视频数据水平反转、垂直反转、设置旋转角度。
好多开发者搞不清楚特别是水平反转和垂直反转,以下我们以图例的形式,做个效果展示。
先看原始图像:
水平反转后:
垂直反转后:
按照设定角度旋转(90°、180°、270°):
以C++的接口为例,设计如下:
/**上下反转(垂直反转)*is_flip: 1:表示反转, 0:表示不反转*/NT_UINT32(NT_API *SetFlipVertical)(NT_HANDLE handle, NT_INT32 is_flip);/**水平反转*is_flip: 1:表示反转, 0:表示不反转*/NT_UINT32(NT_API *SetFlipHorizontal)(NT_HANDLE handle, NT_INT32 is_flip);/*设置旋转,顺时针旋转degress: 设置0, 90, 180, 270度有效,其他值无效注意:除了0度,其他角度播放会耗费更多CPU接口调用成功返回NT_ERC_OK*/NT_UINT32(NT_API* SetRotation)(NT_HANDLE handle, NT_INT32 degress);
以上接口设计,考虑到图像出来后,才可以知道要怎么调整,设计成了可实时调用的接口模式。
具体调用逻辑非常简单:
player_api_.SetFlipVertical(player_handle_, BST_CHECKED == btn_check_flip_vertical_.GetCheck() ? 1 :0 );player_api_.SetFlipHorizontal(player_handle_, BST_CHECKED == btn_check_flip_horizontal_.GetCheck() ? 1 : 0);player_api_.SetRotation(player_handle_, rotate_degrees_);
旋转角度按钮逻辑:
void CSmartPlayerDlg::OnBnClickedButtonRotation()
{rotate_degrees_ += 90;rotate_degrees_ = rotate_degrees_ % 360;if (0 == rotate_degrees_){btn_rotation_.SetWindowText(_T("旋转90度"));}else if (90 == rotate_degrees_){btn_rotation_.SetWindowText(_T("旋转180度"));}else if (180 == rotate_degrees_){btn_rotation_.SetWindowText(_T("旋转270度"));}else if (270 == rotate_degrees_){btn_rotation_.SetWindowText(_T("不旋转"));}if ( player_handle_ != NULL ){player_api_.SetRotation(player_handle_, rotate_degrees_);}
}
总的来说,实现难度不大,此外,我们针对视频数据,还设计了只解关键帧、按照视频宽高scale显示图像,最大限度的方便用户使用。
/**设置只解码视频关键帧*is_only_dec_key_frame: 1:表示只解码关键帧, 0:表示都解码, 默认是0*成功返回NT_ERC_OK*/NT_UINT32(NT_API *SetOnlyDecodeVideoKeyFrame)(NT_HANDLE handle, NT_INT32 is_only_dec_key_frame);
/*设置视频画面的填充模式,如填充整个绘制窗口、等比例填充绘制窗口,如不设置,默认填充整个绘制窗口handle: 播放句柄mode: 0: 填充整个绘制窗口; 1: 等比例填充绘制窗口, 默认值是0成功返回NT_ERC_OK*/NT_UINT32 (NT_API *SetRenderScaleMode)(NT_HANDLE handle, NT_INT32 mode);
如果以上数据都还不满足开发者或终端用户的需求,我们还可以把数据(YUV/RGB)回调上来,用户自行处理。
player_api_.SetVideoFrameCallBack(player_handle_, NT_SP_E_VIDEO_FRAME_FORMAT_RGB32,GetSafeHwnd(), SM_SDKVideoFrameHandle);
extern "C" NT_VOID NT_CALLBACK SM_SDKVideoFrameHandle(NT_HANDLE handle, NT_PVOID userData, NT_UINT32 status,const NT_SP_VideoFrame* frame)
{/*if (frame != NULL){std::ostringstream ss;ss << "Receive frame time_stamp:" << frame->timestamp_ << "ms" << "\r\n";OutputDebugStringA(ss.str().c_str());}*/if ( frame != NULL ){if ( NT_SP_E_VIDEO_FRAME_FORMAT_RGB32 == frame->format_&& frame->plane0_ != NULL&& frame->stride0_ > 0&& frame->height_ > 0 ){std::unique_ptr<nt_rgb32_image > pImage(new nt_rgb32_image());pImage->size_ = frame->stride0_* frame->height_;pImage->data_ = new NT_BYTE[pImage->size_];memcpy(pImage->data_, frame->plane0_, pImage->size_);pImage->width_ = frame->width_;pImage->height_ = frame->height_;pImage->stride_ = frame->stride0_;HWND hwnd = (HWND)userData;if ( hwnd != NULL && ::IsWindow(hwnd) ){::PostMessage(hwnd, WM_USER_SDK_RGB32_IMAGE, (WPARAM)handle, (LPARAM)pImage.release());}}}
}
有了这些数据接口的加持,播放端对数据处理非常方便。