/*
* test-pipe-manager.cpp -test pipe manager
*
* Copyright (c) 2016 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author: Yinhang Liu <yinhangx.liu@intel.com>
*/
#include <pipe_manager.h>
#include <smart_analyzer_loader.h>
#include <ocl/cl_post_image_processor.h>
#if HAVE_LIBDRM
#include <drm_display.h>
#endif
#include <getopt.h>
#include <test_common.h>
#include <signal.h>
#include <stdio.h>
#define DEFAULT_FPT_BUF_COUNT 32
using namespace XCam;
static bool is_stop = false;
struct FileFP {
FILE *fp;
FileFP ()
: fp (NULL)
{}
~FileFP ()
{
if (fp)
fclose (fp);
fp = NULL;
}
};
class MainPipeManager
: public PipeManager
{
public:
MainPipeManager ()
: _image_width (0)
, _image_height (0)
, _enable_display (false)
{
#if HAVE_LIBDRM
_display = DrmDisplay::instance ();
#endif
XCAM_OBJ_PROFILING_INIT;
}
void set_image_width (uint32_t image_width) {
_image_width = image_width;
}
void set_image_height (uint32_t image_height) {
_image_height = image_height;
}
void enable_display (bool value) {
_enable_display = value;
}
#if HAVE_LIBDRM
void set_display_mode (DrmDisplayMode mode) {
_display->set_display_mode (mode);
}
#endif
protected:
virtual void post_buffer (const SmartPtr<VideoBuffer> &buf);
int display_buf (const SmartPtr<VideoBuffer> &buf);
private:
uint32_t _image_width;
uint32_t _image_height;
bool _enable_display;
#if HAVE_LIBDRM
SmartPtr<DrmDisplay> _display;
#endif
XCAM_OBJ_PROFILING_DEFINES;
};
void
MainPipeManager::post_buffer (const SmartPtr<VideoBuffer> &buf)
{
FPS_CALCULATION (fps_buf, XCAM_OBJ_DUR_FRAME_NUM);
XCAM_OBJ_PROFILING_START;
if (_enable_display)
display_buf (buf);
XCAM_OBJ_PROFILING_END("main_pipe_manager_display", XCAM_OBJ_DUR_FRAME_NUM);
}
int
MainPipeManager::display_buf (const SmartPtr<VideoBuffer> &data)
{
#if HAVE_LIBDRM
XCamReturn ret = XCAM_RETURN_NO_ERROR;
SmartPtr<VideoBuffer> buf = data;
const VideoBufferInfo & frame_info = buf->get_video_info ();
struct v4l2_rect rect = { 0, 0, frame_info.width, frame_info.height};
if (!_display->is_render_inited ()) {
ret = _display->render_init (0, 0, this->_image_width, this->_image_height,
frame_info.format, &rect);
CHECK (ret, "display failed on render_init");
}
ret = _display->render_setup_frame_buffer (buf);
CHECK (ret, "display failed on framebuf set");
ret = _display->render_buffer (buf);
CHECK (ret, "display failed on rendering");
#else
XCAM_UNUSED (data);
#endif
return 0;
}
XCamReturn
read_buf (SmartPtr<VideoBuffer> &buf, FileFP &file)
{
const VideoBufferInfo info = buf->get_video_info ();
VideoBufferPlanarInfo planar;
XCamReturn ret = XCAM_RETURN_NO_ERROR;
uint8_t *memory = buf->map ();
for (uint32_t index = 0; index < info.components; index++) {
info.get_planar_info (planar, index);
uint32_t line_bytes = planar.width * planar.pixel_bytes;
for (uint32_t i = 0; i < planar.height; i++) {
if (fread (memory + info.offsets [index] + i * info.strides [index], 1, line_bytes, file.fp) != line_bytes) {
if (feof (file.fp)) {
fseek (file.fp, 0, SEEK_SET);
ret = XCAM_RETURN_BYPASS;
} else {
XCAM_LOG_ERROR ("read file failed, size doesn't match");
ret = XCAM_RETURN_ERROR_FILE;
}
goto done;
}
}
}
done:
buf->unmap ();
return ret;
}
void pipe_stop_handler(int sig)
{
XCAM_UNUSED (sig);
is_stop = true;
}
void print_help (const char *bin_name)
{
printf ("Usage: %s [--format=NV12] [--width=1920] ...\n"
"\t --format specify output pixel format, default is NV12\n"
"\t --width specify input image width, default is 1920\n"
"\t --height specify input image height, default is 1080\n"
"\t --fake-input specify the path of image as fake source\n"
"\t --defog-mode specify defog mode\n"
"\t select from [disabled, retinex, dcp], default is [disabled]\n"
"\t --wavelet-mode specify wavelet denoise mode, default is disable\n"
"\t select from [0:disable, 1:Hat Y, 2:Hat UV, 3:Haar Y, 4:Haar UV, 5:Haar YUV, 6:Haar Bayes Shrink]\n"
"\t --3d-denoise specify 3D Denoise mode\n"
"\t select from [disabled, yuv, uv], default is [disabled]\n"
"\t --enable-wireframe enable wire frame\n"
"\t --enable-warp enable image warp\n"
"\t --display-mode display mode\n"
"\t select from [primary, overlay], default is [primary]\n"
"\t -p enable local display, need root privilege\n"
"\t -h help\n"
, bin_name);
}
int main (int argc, char *argv[])
{
const char *bin_name = argv[0];
XCamReturn ret = XCAM_RETURN_NO_ERROR;
VideoBufferInfo buf_info;
SmartPtr<VideoBuffer> video_buf;
SmartPtr<SmartAnalyzer> smart_analyzer;
SmartPtr<CLPostImageProcessor> cl_post_processor;
SmartPtr<BufferPool> buf_pool;
uint32_t pixel_format = V4L2_PIX_FMT_NV12;
uint32_t image_width = 1920;
uint32_t image_height = 1080;
bool need_display = false;
#if HAVE_LIBDRM
DrmDisplayMode display_mode = DRM_DISPLAY_MODE_PRIMARY;
#endif
const char *input_path = NULL;
FileFP input_fp;
uint32_t defog_mode = 0;
CLWaveletBasis wavelet_mode = CL_WAVELET_DISABLED;
uint32_t wavelet_channel = CL_IMAGE_CHANNEL_UV;
bool wavelet_bayes_shrink = false;
uint32_t denoise_3d_mode = 0;
uint8_t denoise_3d_ref_count = 3;
bool enable_wireframe = false;
bool enable_image_warp = false;
int opt;
const char *short_opts = "ph";
const struct option long_opts [] = {
{"format", required_argument, NULL, 'F'},
{"width", required_argument, NULL, 'W'},
{"height", required_argument, NULL, 'H'},
{"fake-input", required_argument, NULL, 'A'},
{"defog-mode", required_argument, NULL, 'D'},
{"wavelet-mode", required_argument, NULL, 'V'},
{"3d-denoise", required_argument, NULL, 'N'},
{"enable-wireframe", no_argument, NULL, 'I'},
{"enable-warp", no_argument, NULL, 'S'},
{"display-mode", required_argument, NULL, 'P'},
{NULL, 0, NULL, 0}
};
while ((opt = getopt_long (argc, argv, short_opts, long_opts, NULL)) != -1) {
switch (opt) {
case 'F': {
XCAM_ASSERT (optarg);
CHECK_EXP ((strlen (optarg) == 4), "invalid pixel format\n");
pixel_format = v4l2_fourcc ((unsigned) optarg[0],
(unsigned) optarg[1],
(unsigned) optarg[2],
(unsigned) optarg[3]);
break;
}
case 'W': {
XCAM_ASSERT (optarg);
image_width = atoi (optarg);
break;
}
case 'H': {
XCAM_ASSERT (optarg);
image_height = atoi (optarg);
break;
}
case 'A': {
XCAM_ASSERT (optarg);
XCAM_LOG_INFO ("use image %s as input source", optarg);
input_path = optarg;
break;
}
case 'D': {
XCAM_ASSERT (optarg);
if (!strcmp (optarg, "disabled"))
defog_mode = CLPostImageProcessor::DefogDisabled;
else if (!strcmp (optarg, "retinex"))
defog_mode = CLPostImageProcessor::DefogRetinex;
else if (!strcmp (optarg, "dcp"))
defog_mode = CLPostImageProcessor::DefogDarkChannelPrior;
else {
print_help (bin_name);
return -1;
}
break;
}
case 'V': {
XCAM_ASSERT (optarg);
if (atoi(optarg) < 0 || atoi(optarg) > 255) {
print_help (bin_name);
return -1;
}
if (atoi(optarg) == 1) {
wavelet_mode = CL_WAVELET_HAT;
wavelet_channel = CL_IMAGE_CHANNEL_Y;
} else if (atoi(optarg) == 2) {
wavelet_mode = CL_WAVELET_HAT;
wavelet_channel = CL_IMAGE_CHANNEL_UV;
} else if (atoi(optarg) == 3) {
wavelet_mode = CL_WAVELET_HAAR;
wavelet_channel = CL_IMAGE_CHANNEL_Y;
} else if (atoi(optarg) == 4) {
wavelet_mode = CL_WAVELET_HAAR;
wavelet_channel = CL_IMAGE_CHANNEL_UV;
} else if (atoi(optarg) == 5) {
wavelet_mode = CL_WAVELET_HAAR;
wavelet_channel = CL_IMAGE_CHANNEL_UV | CL_IMAGE_CHANNEL_Y;
} else if (atoi(optarg) == 6) {
wavelet_mode = CL_WAVELET_HAAR;
wavelet_channel = CL_IMAGE_CHANNEL_UV | CL_IMAGE_CHANNEL_Y;
wavelet_bayes_shrink = true;
} else {
wavelet_mode = CL_WAVELET_DISABLED;
}
break;
}
case 'N': {
XCAM_ASSERT (optarg);
if (!strcmp (optarg, "disabled"))
denoise_3d_mode = CLPostImageProcessor::Denoise3DDisabled;
else if (!strcmp (optarg, "yuv"))
denoise_3d_mode = CLPostImageProcessor::Denoise3DYuv;
else if (!strcmp (optarg, "uv"))
denoise_3d_mode = CLPostImageProcessor::Denoise3DUV;
else {
print_help (bin_name);
return -1;
}
break;
}
case 'I': {
enable_wireframe = true;
break;
}
case 'S': {
enable_image_warp = true;
break;
}
case 'P': {
#if HAVE_LIBDRM
XCAM_ASSERT (optarg);
if (!strcmp (optarg, "primary"))
display_mode = DRM_DISPLAY_MODE_PRIMARY;
else if (!strcmp (optarg, "overlay"))
display_mode = DRM_DISPLAY_MODE_OVERLAY;
else {
print_help (bin_name);
return -1;
}
#else
XCAM_LOG_WARNING ("preview is not supported");
#endif
break;
}
case 'p': {
#if HAVE_LIBDRM
need_display = true;
#else
XCAM_LOG_WARNING ("preview is not supported, disable preview now");
need_display = false;
#endif
break;
}
case 'h':
print_help (bin_name);
return 0;
default:
print_help (bin_name);
return -1;
}
}
signal (SIGINT, pipe_stop_handler);
if (!input_path) {
XCAM_LOG_ERROR ("path of image is NULL");
return -1;
}
input_fp.fp = fopen (input_path, "rb");
if (!input_fp.fp) {
XCAM_LOG_ERROR ("failed to open file: %s", XCAM_STR (input_path));
return -1;
}
SmartPtr<MainPipeManager> pipe_manager = new MainPipeManager ();
pipe_manager->set_image_width (image_width);
pipe_manager->set_image_height (image_height);
SmartHandlerList smart_handlers = SmartAnalyzerLoader::load_smart_handlers (DEFAULT_SMART_ANALYSIS_LIB_DIR);
if (!smart_handlers.empty () ) {
smart_analyzer = new SmartAnalyzer ();
if (smart_analyzer.ptr ()) {
SmartHandlerList::iterator i_handler = smart_handlers.begin ();
for (; i_handler != smart_handlers.end (); ++i_handler) {
XCAM_ASSERT ((*i_handler).ptr ());
smart_analyzer->add_handler (*i_handler);
}
} else {
XCAM_LOG_INFO ("load smart analyzer(%s) failed", DEFAULT_SMART_ANALYSIS_LIB_DIR);
}
}
if (smart_analyzer.ptr ()) {
if (smart_analyzer->prepare_handlers () != XCAM_RETURN_NO_ERROR) {
XCAM_LOG_WARNING ("analyzer(%s) prepare handlers failed", smart_analyzer->get_name ());
}
pipe_manager->set_smart_analyzer (smart_analyzer);
}
cl_post_processor = new CLPostImageProcessor ();
cl_post_processor->set_stats_callback (pipe_manager);
cl_post_processor->set_defog_mode ((CLPostImageProcessor::CLDefogMode) defog_mode);
cl_post_processor->set_wavelet (wavelet_mode, wavelet_channel, wavelet_bayes_shrink);
cl_post_processor->set_3ddenoise_mode ((CLPostImageProcessor::CL3DDenoiseMode) denoise_3d_mode, denoise_3d_ref_count);
cl_post_processor->set_wireframe (enable_wireframe);
cl_post_processor->set_image_warp (enable_image_warp);
if (smart_analyzer.ptr () && (enable_wireframe || enable_image_warp)) {
cl_post_processor->set_scaler (true);
cl_post_processor->set_scaler_factor (640.0 / image_width);
}
pipe_manager->add_image_processor (cl_post_processor);
buf_info.init (pixel_format, image_width, image_height);
buf_pool = new CLVideoBufferPool ();
XCAM_ASSERT (buf_pool.ptr ());
if (!buf_pool->set_video_info (buf_info) || !buf_pool->reserve (DEFAULT_FPT_BUF_COUNT)) {
XCAM_LOG_ERROR ("init buffer pool failed");
return -1;
}
if (need_display) {
need_display = false;
XCAM_LOG_WARNING ("CLVideoBuffer doesn't support local preview, disable local preview now");
}
if (need_display) {
#if HAVE_LIBDRM
if (DrmDisplay::set_preview (need_display)) {
pipe_manager->set_display_mode (display_mode);
cl_post_processor->set_output_format (V4L2_PIX_FMT_XBGR32);
} else {
need_display = false;
XCAM_LOG_WARNING ("set preview failed, disable local preview now");
}
#else
XCAM_LOG_WARNING ("preview is not supported, disable preview now");
need_display = false;
#endif
}
pipe_manager->enable_display (need_display);
ret = pipe_manager->start ();
CHECK (ret, "pipe manager start failed");
while (!is_stop) {
video_buf = buf_pool->get_buffer (buf_pool);
XCAM_ASSERT (video_buf.ptr ());
ret = read_buf (video_buf, input_fp);
if (ret == XCAM_RETURN_BYPASS) {
ret = read_buf (video_buf, input_fp);
}
if (ret == XCAM_RETURN_NO_ERROR)
pipe_manager->push_buffer (video_buf);
}
ret = pipe_manager->stop();
CHECK (ret, "pipe manager stop failed");
return 0;
}