├── Doxyfile ├── INSTALL ├── Makefile ├── README ├── capture.h ├── capture_fb.c ├── capture_getinfo.c ├── capture_sdl.c ├── capture_simple.c ├── color.c ├── color.h ├── debug-msg.h ├── fb_utils.c ├── fb_utils.h ├── huffman.h ├── main.c ├── my_types.h ├── utils.c ├── utils.h ├── v4l2uvc.c ├── v4l2uvc.h ├── yuv2rgb.c └── yuv2rgb.h /Doxyfile: -------------------------------------------------------------------------------- 1 | # Doxyfile 1.7.3 2 | 3 | #--------------------------------------------------------------------------- 4 | # Project related configuration options 5 | #--------------------------------------------------------------------------- 6 | DOXYFILE_ENCODING = UTF-8 7 | PROJECT_NAME = "视频采集及显示" 8 | PROJECT_NUMBER = "1.0 beta" 9 | PROJECT_BRIEF = "Linux平台视频采集及其显示" 10 | PROJECT_LOGO = 11 | OUTPUT_DIRECTORY = camera-html 12 | CREATE_SUBDIRS = NO 13 | OUTPUT_LANGUAGE = Chinese 14 | BRIEF_MEMBER_DESC = YES 15 | REPEAT_BRIEF = YES 16 | ABBREVIATE_BRIEF = 17 | ALWAYS_DETAILED_SEC = NO 18 | INLINE_INHERITED_MEMB = NO 19 | FULL_PATH_NAMES = YES 20 | STRIP_FROM_PATH = 21 | STRIP_FROM_INC_PATH = 22 | SHORT_NAMES = NO 23 | JAVADOC_AUTOBRIEF = NO 24 | QT_AUTOBRIEF = NO 25 | MULTILINE_CPP_IS_BRIEF = NO 26 | INHERIT_DOCS = YES 27 | SEPARATE_MEMBER_PAGES = NO 28 | TAB_SIZE = 8 29 | ALIASES = 30 | OPTIMIZE_OUTPUT_FOR_C = NO 31 | OPTIMIZE_OUTPUT_JAVA = NO 32 | OPTIMIZE_FOR_FORTRAN = NO 33 | OPTIMIZE_OUTPUT_VHDL = NO 34 | EXTENSION_MAPPING = 35 | BUILTIN_STL_SUPPORT = NO 36 | CPP_CLI_SUPPORT = NO 37 | SIP_SUPPORT = NO 38 | IDL_PROPERTY_SUPPORT = YES 39 | DISTRIBUTE_GROUP_DOC = NO 40 | SUBGROUPING = YES 41 | TYPEDEF_HIDES_STRUCT = NO 42 | SYMBOL_CACHE_SIZE = 0 43 | #--------------------------------------------------------------------------- 44 | # Build related configuration options 45 | #--------------------------------------------------------------------------- 46 | EXTRACT_ALL = NO 47 | EXTRACT_PRIVATE = NO 48 | EXTRACT_STATIC = NO 49 | EXTRACT_LOCAL_CLASSES = YES 50 | EXTRACT_LOCAL_METHODS = NO 51 | EXTRACT_ANON_NSPACES = NO 52 | HIDE_UNDOC_MEMBERS = NO 53 | HIDE_UNDOC_CLASSES = NO 54 | HIDE_FRIEND_COMPOUNDS = NO 55 | HIDE_IN_BODY_DOCS = NO 56 | INTERNAL_DOCS = NO 57 | CASE_SENSE_NAMES = YES 58 | HIDE_SCOPE_NAMES = NO 59 | SHOW_INCLUDE_FILES = YES 60 | FORCE_LOCAL_INCLUDES = NO 61 | INLINE_INFO = YES 62 | SORT_MEMBER_DOCS = YES 63 | SORT_BRIEF_DOCS = NO 64 | SORT_MEMBERS_CTORS_1ST = NO 65 | SORT_GROUP_NAMES = NO 66 | SORT_BY_SCOPE_NAME = NO 67 | STRICT_PROTO_MATCHING = NO 68 | GENERATE_TODOLIST = YES 69 | GENERATE_TESTLIST = YES 70 | GENERATE_BUGLIST = YES 71 | GENERATE_DEPRECATEDLIST= YES 72 | ENABLED_SECTIONS = 73 | MAX_INITIALIZER_LINES = 30 74 | SHOW_USED_FILES = YES 75 | SHOW_DIRECTORIES = NO 76 | SHOW_FILES = YES 77 | SHOW_NAMESPACES = YES 78 | FILE_VERSION_FILTER = 79 | LAYOUT_FILE = 80 | #--------------------------------------------------------------------------- 81 | # configuration options related to warning and progress messages 82 | #--------------------------------------------------------------------------- 83 | QUIET = NO 84 | WARNINGS = YES 85 | WARN_IF_UNDOCUMENTED = YES 86 | WARN_IF_DOC_ERROR = YES 87 | WARN_NO_PARAMDOC = NO 88 | WARN_FORMAT = "$file:$line: $text" 89 | WARN_LOGFILE = 90 | #--------------------------------------------------------------------------- 91 | # configuration options related to the input files 92 | #--------------------------------------------------------------------------- 93 | INPUT = 94 | INPUT_ENCODING = UTF-8 95 | FILE_PATTERNS = 96 | RECURSIVE = NO 97 | EXCLUDE = 98 | EXCLUDE_SYMLINKS = NO 99 | EXCLUDE_PATTERNS = 100 | EXCLUDE_SYMBOLS = 101 | EXAMPLE_PATH = 102 | EXAMPLE_PATTERNS = 103 | EXAMPLE_RECURSIVE = NO 104 | IMAGE_PATH = image_dir 105 | INPUT_FILTER = 106 | FILTER_PATTERNS = 107 | FILTER_SOURCE_FILES = NO 108 | FILTER_SOURCE_PATTERNS = 109 | #--------------------------------------------------------------------------- 110 | # configuration options related to source browsing 111 | #--------------------------------------------------------------------------- 112 | SOURCE_BROWSER = NO 113 | INLINE_SOURCES = NO 114 | STRIP_CODE_COMMENTS = YES 115 | REFERENCED_BY_RELATION = NO 116 | REFERENCES_RELATION = NO 117 | REFERENCES_LINK_SOURCE = YES 118 | USE_HTAGS = NO 119 | VERBATIM_HEADERS = YES 120 | #--------------------------------------------------------------------------- 121 | # configuration options related to the alphabetical class index 122 | #--------------------------------------------------------------------------- 123 | ALPHABETICAL_INDEX = YES 124 | COLS_IN_ALPHA_INDEX = 5 125 | IGNORE_PREFIX = 126 | #--------------------------------------------------------------------------- 127 | # configuration options related to the HTML output 128 | #--------------------------------------------------------------------------- 129 | GENERATE_HTML = YES 130 | HTML_OUTPUT = . 131 | HTML_FILE_EXTENSION = .html 132 | HTML_HEADER = 133 | HTML_FOOTER = 134 | HTML_STYLESHEET = 135 | HTML_COLORSTYLE_HUE = 220 136 | HTML_COLORSTYLE_SAT = 100 137 | HTML_COLORSTYLE_GAMMA = 80 138 | HTML_TIMESTAMP = YES 139 | HTML_ALIGN_MEMBERS = YES 140 | HTML_DYNAMIC_SECTIONS = NO 141 | GENERATE_DOCSET = NO 142 | DOCSET_FEEDNAME = "Doxygen generated docs" 143 | DOCSET_BUNDLE_ID = org.doxygen.Project 144 | DOCSET_PUBLISHER_ID = org.doxygen.Publisher 145 | DOCSET_PUBLISHER_NAME = Publisher 146 | GENERATE_HTMLHELP = NO 147 | CHM_FILE = 148 | HHC_LOCATION = 149 | GENERATE_CHI = NO 150 | CHM_INDEX_ENCODING = 151 | BINARY_TOC = NO 152 | TOC_EXPAND = NO 153 | GENERATE_QHP = NO 154 | QCH_FILE = 155 | QHP_NAMESPACE = org.doxygen.Project 156 | QHP_VIRTUAL_FOLDER = doc 157 | QHP_CUST_FILTER_NAME = 158 | QHP_CUST_FILTER_ATTRS = 159 | QHP_SECT_FILTER_ATTRS = 160 | QHG_LOCATION = 161 | GENERATE_ECLIPSEHELP = NO 162 | ECLIPSE_DOC_ID = org.doxygen.Project 163 | DISABLE_INDEX = NO 164 | ENUM_VALUES_PER_LINE = 4 165 | GENERATE_TREEVIEW = NO 166 | USE_INLINE_TREES = NO 167 | TREEVIEW_WIDTH = 250 168 | EXT_LINKS_IN_WINDOW = NO 169 | FORMULA_FONTSIZE = 10 170 | FORMULA_TRANSPARENT = YES 171 | USE_MATHJAX = NO 172 | MATHJAX_RELPATH = http://www.mathjax.org/mathjax 173 | SEARCHENGINE = YES 174 | SERVER_BASED_SEARCH = NO 175 | #--------------------------------------------------------------------------- 176 | # configuration options related to the LaTeX output 177 | #--------------------------------------------------------------------------- 178 | GENERATE_LATEX = NO 179 | LATEX_OUTPUT = latex 180 | LATEX_CMD_NAME = latex 181 | MAKEINDEX_CMD_NAME = makeindex 182 | COMPACT_LATEX = NO 183 | PAPER_TYPE = a4 184 | EXTRA_PACKAGES = 185 | LATEX_HEADER = 186 | PDF_HYPERLINKS = YES 187 | USE_PDFLATEX = YES 188 | LATEX_BATCHMODE = NO 189 | LATEX_HIDE_INDICES = NO 190 | LATEX_SOURCE_CODE = NO 191 | #--------------------------------------------------------------------------- 192 | # configuration options related to the RTF output 193 | #--------------------------------------------------------------------------- 194 | GENERATE_RTF = NO 195 | RTF_OUTPUT = rtf 196 | COMPACT_RTF = NO 197 | RTF_HYPERLINKS = NO 198 | RTF_STYLESHEET_FILE = 199 | RTF_EXTENSIONS_FILE = 200 | #--------------------------------------------------------------------------- 201 | # configuration options related to the man page output 202 | #--------------------------------------------------------------------------- 203 | GENERATE_MAN = NO 204 | MAN_OUTPUT = man 205 | MAN_EXTENSION = .3 206 | MAN_LINKS = NO 207 | #--------------------------------------------------------------------------- 208 | # configuration options related to the XML output 209 | #--------------------------------------------------------------------------- 210 | GENERATE_XML = NO 211 | XML_OUTPUT = xml 212 | XML_SCHEMA = 213 | XML_DTD = 214 | XML_PROGRAMLISTING = YES 215 | #--------------------------------------------------------------------------- 216 | # configuration options for the AutoGen Definitions output 217 | #--------------------------------------------------------------------------- 218 | GENERATE_AUTOGEN_DEF = NO 219 | #--------------------------------------------------------------------------- 220 | # configuration options related to the Perl module output 221 | #--------------------------------------------------------------------------- 222 | GENERATE_PERLMOD = NO 223 | PERLMOD_LATEX = NO 224 | PERLMOD_PRETTY = YES 225 | PERLMOD_MAKEVAR_PREFIX = 226 | #--------------------------------------------------------------------------- 227 | # Configuration options related to the preprocessor 228 | #--------------------------------------------------------------------------- 229 | ENABLE_PREPROCESSING = YES 230 | MACRO_EXPANSION = NO 231 | EXPAND_ONLY_PREDEF = NO 232 | SEARCH_INCLUDES = YES 233 | INCLUDE_PATH = 234 | INCLUDE_FILE_PATTERNS = 235 | PREDEFINED = 236 | EXPAND_AS_DEFINED = 237 | SKIP_FUNCTION_MACROS = YES 238 | #--------------------------------------------------------------------------- 239 | # Configuration::additions related to external references 240 | #--------------------------------------------------------------------------- 241 | TAGFILES = 242 | GENERATE_TAGFILE = 243 | ALLEXTERNALS = NO 244 | EXTERNAL_GROUPS = YES 245 | PERL_PATH = /usr/bin/perl 246 | #--------------------------------------------------------------------------- 247 | # Configuration options related to the dot tool 248 | #--------------------------------------------------------------------------- 249 | CLASS_DIAGRAMS = YES 250 | MSCGEN_PATH = 251 | HIDE_UNDOC_RELATIONS = YES 252 | HAVE_DOT = NO 253 | DOT_NUM_THREADS = 0 254 | DOT_FONTNAME = Helvetica 255 | DOT_FONTSIZE = 10 256 | DOT_FONTPATH = 257 | CLASS_GRAPH = YES 258 | COLLABORATION_GRAPH = YES 259 | GROUP_GRAPHS = YES 260 | UML_LOOK = NO 261 | TEMPLATE_RELATIONS = NO 262 | INCLUDE_GRAPH = YES 263 | INCLUDED_BY_GRAPH = YES 264 | CALL_GRAPH = NO 265 | CALLER_GRAPH = NO 266 | GRAPHICAL_HIERARCHY = YES 267 | DIRECTORY_GRAPH = YES 268 | DOT_IMAGE_FORMAT = png 269 | DOT_PATH = 270 | DOTFILE_DIRS = 271 | MSCFILE_DIRS = 272 | DOT_GRAPH_MAX_NODES = 50 273 | MAX_DOT_GRAPH_DEPTH = 0 274 | DOT_TRANSPARENT = NO 275 | DOT_MULTI_TARGETS = NO 276 | GENERATE_LEGEND = YES 277 | DOT_CLEANUP = YES 278 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | 编译: 2 | $ make 3 | 生成的可执行文件为capture 4 | 5 | 执行程序: 6 | $ ./capture 7 | 8 | 清除编译过程的目标文件,请输入 9 | $ make clean 10 | 11 | 注:请务必首先修改Makefile的CROSS_COMPILE和CFLAGS += -mfpu=neon!!! 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # (C) Copyleft 2011 2012 2013 2014 2015 2016 2017 2 | # Late Lee from http://www.latelee.org 3 | # 4 | # A simple Makefile for *ONE* project(c or/and cpp file) in *ONE* or *MORE* directory 5 | # 6 | # note: 7 | # you can put head file(s) in 'include' directory, so it looks 8 | # a little neat. 9 | # 10 | # usage: 11 | # $ make 12 | # $ make V=1 # verbose ouput 13 | # $ make CROSS_COMPILE=arm-arago-linux-gnueabi- # cross compile for ARM, etc. 14 | # $ make debug=y # debug 15 | # $ make SRC_DIR1=foo SRC_DIR2=bar SRC_DIR3=crc 16 | # 17 | # log 18 | # 2013-05-14 sth about debug... 19 | # 2016-02-29 sth for c/c++ multi diretory 20 | # 2017-04-17 -s for .a/.so if no debug 21 | # 2017-05-05 Add V for verbose ouput 22 | ############################################################################### 23 | 24 | # !!!=== cross compile... 25 | CROSS_COMPILE ?= arm-linux- 26 | 27 | CC = $(CROSS_COMPILE)gcc 28 | CXX = $(CROSS_COMPILE)g++ 29 | AR = $(CROSS_COMPILE)ar 30 | 31 | ARFLAGS = -cr 32 | RM = -rm -rf 33 | MAKE = make 34 | 35 | # !!!=== 36 | # target executable file or .a or .so 37 | target = capture 38 | 39 | # !!!=== 40 | # compile flags 41 | CFLAGS := -Wall -Wfatal-errors 42 | 43 | #**************************************************************************** 44 | # debug can be set to y to include debugging info, or n otherwise 45 | debug := y 46 | 47 | #**************************************************************************** 48 | 49 | ifeq ($(debug), y) 50 | CFLAGS += -ggdb -rdynamic 51 | else 52 | CFLAGS += -O2 -s 53 | endif 54 | 55 | # !!!=== 56 | DEFS = 57 | 58 | CFLAGS += $(DEFS) 59 | 60 | LIBS := 61 | 62 | LDFLAGS := $(LIBS) 63 | 64 | 65 | # sdl 66 | ifeq ($(SDL),1) 67 | CFLAGS += -DCAPTURE_SDL 68 | LDFLAGS += -lSDL 69 | endif 70 | 71 | # FB 72 | ifeq ($(FB),1) 73 | CFLAGS += -DCAPTURE_FB 74 | endif 75 | 76 | # FB 77 | ifeq ($(YUV),1) 78 | CFLAGS += -DCAPTURE_SAVEYUV 79 | endif 80 | 81 | ifeq ($(JPEG),1) 82 | CFLAGS += -DCAPTURE_SAVEMJPEG 83 | endif 84 | 85 | 86 | CFLAGS += -mfpu=neon 87 | 88 | # !!!=== 89 | INC1 = ./ 90 | INC2 = ./inc 91 | INC3 = 92 | INCDIRS := -I$(INC1) -I$(INC2) 93 | 94 | # !!!=== 95 | CFLAGS += $(INCDIRS) 96 | 97 | # !!!=== 98 | LDFLAGS += -lpthread -lrt 99 | 100 | DYNC_FLAGS += -fpic -shared 101 | 102 | # !!!=== 103 | # source file(s), including c file(s) or cpp file(s) 104 | # you can also use $(wildcard *.c), etc. 105 | SRC_DIR = . 106 | SRC_DIR1 = 107 | SRC_DIR2 = 108 | SRC_DIR3 = 109 | 110 | # ok for c/c++ 111 | SRC = $(wildcard $(SRC_DIR)/*.c $(SRC_DIR)/*.cpp) 112 | SRC+=$(wildcard $(SRC_DIR1)/*.c $(SRC_DIR1)/*.cpp) 113 | SRC+=$(wildcard $(SRC_DIR2)/*.c $(SRC_DIR2)/*.cpp) 114 | SRC+=$(wildcard $(SRC_DIR3)/*.c $(SRC_DIR3)/*.cpp) 115 | 116 | # ok for c/c++ 117 | OBJ = $(patsubst %.c,%.o, $(patsubst %.cpp,%.o, $(SRC))) 118 | 119 | 120 | # !!!=== 121 | # in case all .c/.cpp need g++... 122 | # CC = $(CXX) 123 | 124 | ifeq ($(V),1) 125 | Q= 126 | NQ=true 127 | else 128 | Q= 129 | NQ=echo 130 | endif 131 | 132 | ############################################################################### 133 | 134 | all: $(target) 135 | 136 | $(target): $(OBJ) 137 | 138 | ifeq ($(suffix $(target)), .so) 139 | @$(NQ) "Generating dynamic lib file..." $(notdir $(target)) 140 | $(Q)$(CXX) $(CFLAGS) $^ -o $(target) $(LDFLAGS) $(DYNC_FLAGS) 141 | else ifeq ($(suffix $(target)), .a) 142 | @$(NQ) "Generating static lib file..." $(notdir $(target)) 143 | $(Q)$(AR) $(ARFLAGS) -o $(target) $^ 144 | else 145 | @$(NQ) "Generating executable file..." $(notdir $(target)) 146 | $(Q)$(CXX) $(CFLAGS) $^ -o $(target) $(LDFLAGS) 147 | endif 148 | 149 | # make all .c or .cpp 150 | %.o: %.c 151 | @$(NQ) "Compiling: " $(addsuffix .c, $(basename $(notdir $@))) 152 | $(Q)$(CC) $(CFLAGS) -c $< -o $@ 153 | 154 | %.o: %.cpp 155 | @$(NQ) "Compiling: " $(addsuffix .cpp, $(basename $(notdir $@))) 156 | $(Q)$(CXX) $(CFLAGS) -c $< -o $@ 157 | 158 | clean: 159 | @$(NQ) "Cleaning..." 160 | $(Q)$(RM) $(target) 161 | 162 | # use 'grep -v soapC.o' to skip the file 163 | @find . -iname '*.o' -o -iname '*.bak' -o -iname '*.d' | xargs rm -f 164 | 165 | .PHONY: all clean -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 功能: 2 | 采集UVC摄像头数据并显示在framebuffer上。同时具备保存jpeg或yuv文件功能。(根据运行参数而定) 3 | 4 | 使用说明: 5 | 1、直接运行capture,则会默认采集/dev/video0摄像头数据,打印摄像头参数信息。用户按回车键后进行循环采集,并打印帧率。 6 | 2、如果需要显示,添加--fb参数。 7 | 3、如果要设置采集数据格式,使用-s +格式参数,如设置yuyv:-s yuyv,设置nv12:-s nv12 8 | 4、设置宽高分辨率:-w 宽 -h 高 9 | 5、保存文件格式:-f yuv表示保存为yuv文件,-f jpg表示保存jpeg图片(注:每帧均为一个jpg图片)。 10 | 6、如果以上参数均没有设置,则使用从摄像头里获取的参数。如摄像头出来是yuyv的,采集的格式就是yuyv。 11 | 7、退出按Ctrl+C。 12 | 13 | 完整说明: 14 | # ./capture --help 15 | ./capture: A video capture tool for linux platform version: 1.1 16 | 17 | usage: 18 | ./capture -d [device] -s [format] -w [width] -h [height] -f [save file] --fb(display to framebuffer) 19 | -d video device, eg:/dev/video0 20 | -s video format, eg:[mjpeg|yuyv|nv12|nv21|nv16|nv61] 21 | -w video width, eg:480 22 | -h video height, eg:320 23 | -f save file(yuv or jpg file), eg:[yuv|jpg] 24 | --fb display video to frame buffer device 25 | --help show help info 26 | default: ./capture -d /dev/video0 -w 480 -h 320 27 | 28 | 29 | 设计: 30 | 程序采用分层架构思想,将采集、显示设计成不同模块接口。通过Makefile管理编译,可应用于SDL显示(如PC机采集)。 31 | 代码通过不同的参数可完成不同的功能。 32 | 33 | 流程图: 34 | UVC->MJPEG ->解码为YUYV-> 转换为RGB24格式->转换为RGB16格式,拷贝到framebuffer映射的内存中->看到结果。 35 | \->YUYV ----------------/ 36 | 37 | 38 | 注意事项: 39 | 1、yuv转换rgb未做优化。使用gcc的-O2优先可大大提高速度。将Makefile的debug改为n即可。 40 | 另外,也使用-mfpu=neon,如果不支持,请去掉。 41 | 2、显示所用LCD为rgb565屏幕。 42 | 43 | 其它说明: 44 | 本程序使用V4l2采集,理论上任何支持该驱动的摄像头。但只使用了UVC测试。 45 | 46 | 本程序提供的v4l2采集模块、yuv转换rgb模块、framebuffer操作模块,均可直接用于工程项目中。接口完备、清晰。 47 | -------------------------------------------------------------------------------- /capture.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _CAPTURE_H 3 | #define _CAPTURE_H 4 | 5 | int capture_v4l2getinfo(int argc, char* argv[]); 6 | int capture_v4l2simple(int argc, char* argv[]); 7 | int capture_saveyuv(int argc, char* argv[]); 8 | int capture_savemjpeg(int argc, char* argv[]); 9 | int capture_sdl(int argc, char* argv[]); 10 | int capture_fb(int argc, char* argv[]); 11 | 12 | 13 | #endif /* _CAPTURE_H */ 14 | -------------------------------------------------------------------------------- /capture_fb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * _/_/ _/_/_/ _/_/_/_/_/ 3 | * _/ _/ _/ _/ 4 | * _/ _/ _/ 5 | * _/ _/_/ _/ 6 | * _/ _/ _/ 7 | * _/ _/ _/ _/ _/ 8 | * _/_/ _/_/ _/_/_/ 9 | */ 10 | /** 11 | * Copyleft (C) 2010 Late Lee 12 | * This program is tested on LINUX PLATFORM, WITH GCC 4.x. 13 | * The program is distributed in the hope that it will be 14 | * useful, but WITHOUT ANY WARRANTY. Please feel free to 15 | * use the program, and I feel free to ignore the related 16 | * issues. Any questions or suggestions, or bugs, please 17 | * contact me at 18 | * <$ echo -n "aHR0cDovL3d3dy5sYXRlbGVlLm9yZwo=" | base64 -d> 19 | * or e-mail to 20 | * <$ echo -n "bGF0ZWxlZUAxNjMuY29tCg==" | base64 -d> 21 | * if you want to do this. 22 | * 23 | * @file capture_sdl.c 24 | * @author Late Lee 25 | * @date Sat May 29 2010 26 | * 27 | * @brief none 28 | * 29 | * @test 30 | * 在程序目录中直接输入\p make 即可编译生成\p capture 可执行文件。\n 31 | * 32 | */ 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "v4l2uvc.h" 39 | #include "utils.h" 40 | 41 | #include "fb_utils.h" 42 | #include "yuv2rgb.h" 43 | 44 | 45 | /** 摄像头信息结构体 */ 46 | static struct video_info* vd_info = NULL; 47 | static int fps = 0; 48 | static int g_fmt = -1; 49 | static char g_fmt_name[16] = {0}; 50 | static int g_width = -1; 51 | static int g_height = -1; 52 | static char videodevice[16] = {0}; 53 | static enum v4l2_driver_type g_driver_type = V4L2_DRIVER_UNKNOWN; 54 | 55 | enum SAVE_FILE_T{ 56 | SVAETYPE_YUV = 0, 57 | SVAETYPE_JPG, 58 | }; 59 | 60 | static int g_save_type = -1; 61 | static FILE* fp = NULL; 62 | static char file_name[16] = "raw.yuv"; 63 | 64 | static int g_need_display = 0; 65 | 66 | int g_debug; 67 | 68 | /** 69 | * sig_int - 信号处理函数 70 | * 71 | * @param signum 参数,可不用理会 72 | * @note 本程序中只处理Ctrl+c中断(SIGINT) 73 | */ 74 | static void sig_int(int signum) 75 | { 76 | debug_msg("\ncatch a SIGINT signal, you may be press Ctrl+C.\n"); 77 | debug_msg("ready to quit\n"); 78 | if (vd_info == NULL) 79 | goto end; 80 | 81 | vd_info->is_quit = 1; 82 | // stop.... 83 | v4l2_close(vd_info); 84 | 85 | end: 86 | fb_release(); 87 | if (fp) 88 | { 89 | fclose(fp); 90 | fp = NULL; 91 | } 92 | 93 | exit(0); 94 | } 95 | 96 | static void sig_alarm(int signum) 97 | { 98 | printf("fps=%dfps\n",fps); 99 | fps = 0; 100 | 101 | alarm(1); 102 | } 103 | 104 | /** 105 | * get_info - 获取摄像头信息测试函数,由main函数调用 106 | * 107 | * 108 | * @return 成功返回0,否则返回-1,并提示出错信息 109 | */ 110 | static int get_info(char* videodevice) 111 | { 112 | struct video_info* vd_info = (struct video_info *) calloc(1, sizeof(struct video_info)); 113 | strcpy(vd_info->name, videodevice); 114 | 115 | if (v4l2_open(vd_info) <0) 116 | exit(1); 117 | if (v4l2_get_capability(vd_info) < 0) 118 | exit(1); 119 | 120 | if (v4l2_enum_format(vd_info) < 0) 121 | exit(1); 122 | if (v4l2_get_format(vd_info) < 0) 123 | exit(1); 124 | if (vd_info->is_streaming) /* stop if it is still capturing */ 125 | v4l2_off(vd_info); 126 | 127 | // 用户没有指定格式/分辨率,则从获取到的值中拿 128 | if (g_fmt == -1) 129 | g_fmt = vd_info->format; 130 | if (g_width == -1) 131 | g_width = vd_info->width; 132 | if (g_height == -1) 133 | g_height = vd_info->height; 134 | 135 | g_driver_type = vd_info->driver_type; 136 | 137 | 138 | if (vd_info->frame_buffer) 139 | free(vd_info->frame_buffer); 140 | if (vd_info->tmp_buffer) 141 | free(vd_info->tmp_buffer); 142 | vd_info->frame_buffer = NULL; 143 | 144 | v4l2_close(vd_info); 145 | 146 | free(vd_info); 147 | 148 | return 0; 149 | } 150 | 151 | // todo:由用户指定保存为什么格式 152 | static int my_save_file(struct video_info* vd_info) 153 | { 154 | static int init = 0; 155 | static int cnt = 0; 156 | 157 | if (g_save_type == SVAETYPE_YUV) 158 | { 159 | sprintf(file_name, "raw.yuv"); 160 | if (init == 0) 161 | { 162 | fp = fopen(file_name, "w"); 163 | init = 1; 164 | if (NULL == fp) 165 | unix_error_ret("unable to open the file"); 166 | } 167 | 168 | fwrite(vd_info->frame_buffer, 1, vd_info->frame_size_in, fp); 169 | if (0 && fp) 170 | { 171 | fclose(fp); 172 | fp = NULL; 173 | } 174 | } 175 | // JPEG 176 | else if (g_save_type == SVAETYPE_JPG) 177 | { 178 | sprintf(file_name, "%d.jpg", cnt); 179 | 180 | fp = fopen(file_name, "w"); 181 | if (NULL == fp) 182 | unix_error_ret("unable to open the file"); 183 | 184 | fwrite(vd_info->tmp_buffer, 1, vd_info->buf.bytesused, fp); 185 | //fwrite(vd_info->tmp_buffer, 1, g_width*g_height*3/2, fp); 186 | 187 | 188 | if (fp) 189 | { 190 | fclose(fp); 191 | fp = NULL; 192 | } 193 | } 194 | 195 | debug_msg("writing frame %d...\n", cnt); 196 | cnt++; 197 | 198 | return 0; 199 | } 200 | 201 | static int my_display_process(struct video_info* vd_info) 202 | { 203 | static unsigned char* rgb_buffer = NULL; 204 | unsigned char* tmp_p = NULL; 205 | int rgb_size = 0; 206 | int width = 0; 207 | int height = 0; 208 | int x = 0; 209 | int y = 0; 210 | 211 | width = vd_info->width; 212 | height = vd_info->height; 213 | rgb_size = width*height*3; 214 | 215 | if (rgb_buffer == NULL) 216 | { 217 | rgb_buffer = (unsigned char*)malloc(rgb_size); 218 | if (rgb_buffer == NULL) 219 | { 220 | return -1; 221 | } 222 | } 223 | // 更改格式 224 | if (vd_info->frame_format == V4L2_PIX_FMT_YUYV) 225 | { 226 | yuv_to_rgb24(FMT_YUYV, vd_info->frame_buffer, rgb_buffer, width, height); 227 | } 228 | else if (vd_info->frame_format == V4L2_PIX_FMT_NV12) 229 | { 230 | yuv_to_rgb24(FMT_NV12, vd_info->frame_buffer, rgb_buffer, width, height); 231 | } 232 | else if (vd_info->frame_format == V4L2_PIX_FMT_NV21) 233 | { 234 | yuv_to_rgb24(FMT_NV21, vd_info->frame_buffer, rgb_buffer, width, height); 235 | } 236 | else if (vd_info->frame_format == V4L2_PIX_FMT_NV16) 237 | { 238 | yuv_to_rgb24(FMT_NV16, vd_info->frame_buffer, rgb_buffer, width, height); 239 | } 240 | else if (vd_info->frame_format == V4L2_PIX_FMT_NV61) 241 | { 242 | yuv_to_rgb24(FMT_NV61, vd_info->frame_buffer, rgb_buffer, width, height); 243 | } 244 | 245 | #if 0 246 | 247 | //printf("video: %dx%d fb: %dx%d\n", width, height, fb->width, fb->height); 248 | tmp_pp = rgb16_buffer; 249 | while (y < height) 250 | { 251 | unsigned short color; 252 | tmp_p = rgb_buffer + y*width*3; 253 | for (x=0; x < width; x++) 254 | { 255 | // LCD为rgb565格式,所以要转换 256 | color = make16color(tmp_p[x*3], tmp_p[x*3+1], tmp_p[x*3+2]); 257 | *((unsigned short*)tmp_pp) = color; 258 | tmp_pp += 2; 259 | } 260 | y++; 261 | } 262 | memcpy(fb->fbmem, rgb16_buffer, width*height); 263 | 264 | 265 | #else 266 | // TODO:这里使用逐个像素描点,需要找个好的方法 267 | while (y < height) 268 | { 269 | unsigned short color; 270 | tmp_p = rgb_buffer + y*width*3; 271 | for (x=0; x < width; x++) 272 | { 273 | // LCD为rgb565格式,所以要转换 274 | color = make16color(tmp_p[x*3], tmp_p[x*3+1], tmp_p[x*3+2]); 275 | fb_pixel(x, y, color); 276 | } 277 | y++; 278 | } 279 | #endif 280 | 281 | return 0; 282 | } 283 | 284 | static int my_v4l2_process(struct video_info* vd_info) 285 | { 286 | if (g_need_display) 287 | my_display_process(vd_info); 288 | 289 | 290 | if (g_save_type != -1) 291 | my_save_file(vd_info); 292 | 293 | return 0; 294 | } 295 | 296 | void usage(char* name) 297 | { 298 | printf("%s: A video capture tool for linux platform version: 1.1\n\n", name); 299 | printf("usage:\n"); 300 | printf("%s -d [device] -s [format] -w [width] -h [height] -f [save file] --fb(display to framebuffer)\n", name); 301 | printf("\t -d\tvideo device, eg:/dev/video0\n"); 302 | printf("\t -s\tvideo format, eg:[mjpeg|yuyv|nv12|nv21|nv16|nv61]\n"); 303 | printf("\t -w\tvideo width, eg:480\n"); 304 | printf("\t -h\tvideo height, eg:320\n"); 305 | printf("\t -f\tsave file(yuv or jpg file), eg:[yuv|jpg]\n"); 306 | printf("\t --fb\tdisplay video to frame buffer device\n"); 307 | printf("\t --help\t show help info\n"); 308 | printf("default: %s -d /dev/video0 -w 480 -h 320\n", name); 309 | 310 | exit(0); 311 | } 312 | 313 | 314 | int parsecmd(int argc, char *argv[]) 315 | { 316 | int i = 0; 317 | 318 | strcpy(videodevice, "/dev/video0"); 319 | 320 | if (argc > 1) { 321 | for (i = 1; i < argc; i++) 322 | { 323 | if ((strcmp(argv[i], "-D")==0) || (strcmp(argv[i], "--debug")==0)) 324 | { 325 | g_debug = 1; 326 | } 327 | } 328 | } 329 | 330 | // 331 | /* parse parameters, maybe not the best way but... */ 332 | for (i = 1; i < argc; i++) 333 | { 334 | if (g_debug) 335 | printf("arg %d: \"%s\"\n",i,argv[i]); 336 | // help 337 | if (strcmp(argv[i],"--help")==0) 338 | { 339 | usage(argv[0]); 340 | return 0; 341 | } 342 | // debug 343 | else if ((strcmp(argv[i],"-D")==0) || (strcmp(argv[i],"--debug")==0)) 344 | { 345 | g_debug=1; 346 | } 347 | // string 348 | else if ((strcmp(argv[i],"-d")==0)) 349 | { 350 | if (i+1name, videodevice); 472 | 473 | vd_info->format = g_fmt; //V4L2_PIX_FMT_YUYV; //g_fmt; //g_fmt; //V4L2_PIX_FMT_NV21;//V4L2_PIX_FMT_MJPEG; //V4L2_PIX_FMT_NV12; 474 | vd_info->width = g_width; //g_width; // 640 475 | vd_info->height = g_height;//g_height; //480; 476 | vd_info->driver_type = g_driver_type; 477 | 478 | 479 | printf("====info video fmt: 0x%x res: %dx%d driver type: %d\n", 480 | vd_info->format, vd_info->width, vd_info->height, vd_info->driver_type); 481 | 482 | //v4l2_set_processcb(my_v4l2_process); 483 | 484 | if (v4l2_init(vd_info) < 0) 485 | return -1; 486 | 487 | if (g_need_display) 488 | { 489 | fb_init(); // 标准framebuffer显示 490 | } 491 | 492 | while (1) 493 | { 494 | if (v4l2_readframe(vd_info) < 0) 495 | { 496 | printf("Error grabbing will continue\n"); 497 | //break; 498 | } 499 | my_v4l2_process(vd_info); 500 | fps++; 501 | } 502 | 503 | v4l2_close(vd_info); 504 | 505 | free(vd_info); 506 | 507 | return 0; 508 | } -------------------------------------------------------------------------------- /capture_getinfo.c: -------------------------------------------------------------------------------- 1 | /* 2 | * _/_/ _/_/_/ _/_/_/_/_/ 3 | * _/ _/ _/ _/ 4 | * _/ _/ _/ 5 | * _/ _/_/ _/ 6 | * _/ _/ _/ 7 | * _/ _/ _/ _/ _/ 8 | * _/_/ _/_/ _/_/_/ 9 | */ 10 | /** 11 | * Copyleft (C) 2010 Late Lee 12 | * This program is tested on LINUX PLATFORM, WITH GCC 4.x. 13 | * The program is distributed in the hope that it will be 14 | * useful, but WITHOUT ANY WARRANTY. Please feel free to 15 | * use the program, and I feel free to ignore the related 16 | * issues. Any questions or suggestions, or bugs, please 17 | * contact me at 18 | * <$ echo -n "aHR0cDovL3d3dy5sYXRlbGVlLm9yZwo=" | base64 -d> 19 | * or e-mail to 20 | * <$ echo -n "bGF0ZWxlZUAxNjMuY29tCg==" | base64 -d> 21 | * if you want to do this. 22 | * 23 | * @file capture_sdl.c 24 | * @author Late Lee 25 | * @date Sat May 29 2010 26 | * 27 | * @brief none 28 | * 29 | * @test 30 | * 在程序目录中直接输入\p make 即可编译生成\p capture 可执行文件。\n 31 | * 32 | */ 33 | 34 | #include 35 | #include "v4l2uvc.h" 36 | 37 | 38 | /** 39 | * get_info - 获取摄像头信息测试函数,由main函数调用 40 | * 41 | * 42 | * @return 成功返回0,否则返回-1,并提示出错信息 43 | */ 44 | static int get_info(char* videodevice) 45 | { 46 | struct video_info* vd_info = (struct video_info *) calloc(1, sizeof(struct video_info)); 47 | strcpy(vd_info->name, videodevice); 48 | 49 | if (v4l2_open(vd_info) <0) 50 | exit(1); 51 | if (v4l2_get_capability(vd_info) < 0) 52 | exit(1); 53 | if (v4l2_get_format(vd_info) < 0) 54 | exit(1); 55 | if (vd_info->is_streaming) /* stop if it is still capturing */ 56 | v4l2_off(vd_info); 57 | 58 | if (vd_info->frame_buffer) 59 | free(vd_info->frame_buffer); 60 | if (vd_info->tmp_buffer) 61 | free(vd_info->tmp_buffer); 62 | vd_info->frame_buffer = NULL; 63 | 64 | v4l2_close(vd_info); 65 | 66 | free(vd_info); 67 | 68 | return 0; 69 | } 70 | 71 | /** 72 | * capture_v4l2getinfo - 最简单的测试函数 73 | * 74 | * @return 成功返回0,否则返回-1,并提示出错信息 75 | * @note 76 | * 1、只获取摄像头相关信息; 77 | * 78 | */ 79 | int capture_v4l2getinfo(int argc, char* argv[]) 80 | { 81 | char videodevice[16] = {0}; 82 | strcpy(videodevice, "/dev/video0"); 83 | if (argc == 2) 84 | { 85 | strcpy(videodevice, argv[1]); 86 | } 87 | printf("willl open %s\n", videodevice); 88 | 89 | get_info(videodevice); 90 | 91 | return 0; 92 | } -------------------------------------------------------------------------------- /capture_sdl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * _/_/ _/_/_/ _/_/_/_/_/ 3 | * _/ _/ _/ _/ 4 | * _/ _/ _/ 5 | * _/ _/_/ _/ 6 | * _/ _/ _/ 7 | * _/ _/ _/ _/ _/ 8 | * _/_/ _/_/ _/_/_/ 9 | */ 10 | /** 11 | * Copyleft (C) 2010 Late Lee 12 | * This program is tested on LINUX PLATFORM, WITH GCC 4.x. 13 | * The program is distributed in the hope that it will be 14 | * useful, but WITHOUT ANY WARRANTY. Please feel free to 15 | * use the program, and I feel free to ignore the related 16 | * issues. Any questions or suggestions, or bugs, please 17 | * contact me at 18 | * <$ echo -n "aHR0cDovL3d3dy5sYXRlbGVlLm9yZwo=" | base64 -d> 19 | * or e-mail to 20 | * <$ echo -n "bGF0ZWxlZUAxNjMuY29tCg==" | base64 -d> 21 | * if you want to do this. 22 | * 23 | * @file capture_sdl.c 24 | * @author Late Lee 25 | * @date Sat May 29 2010 26 | * 27 | * @brief none 28 | * 29 | * @test 30 | * 在程序目录中直接输入\p make 即可编译生成\p capture 可执行文件。\n 31 | * 输入\p ./capture即可运行程序,在运行程序之前,请确保插入USB摄像头,并查看\p /dev 目录下 32 | * 是否生成\p video 设备文件(使用命令ls -l /dev | grep video),经测试,在PC中,/dev/video文件 33 | * 是摄像头设备的链接文件名称,程序中即使用这个文件名。\n 34 | * 程序测试环境如下:\n 35 | * 虚拟机fc10 \n 36 | * [latelee@latelee camera-pc-utf]$ uname -a \n 37 | * Linux latelee.latelee.org 2.6.27.5-117.fc10.i686 #1 SMP Tue Nov 18 12:19:59 EST 2008 i686 i686 i386 GNU/Linux \n 38 | * [latelee@latelee camera-pc-utf]$ rpm -q "SDL" \n 39 | * SDL-1.2.13-6.fc10.i386 \n 40 | * [latelee@latelee camera-pc-utf]$ rpm -q "glibc" \n 41 | * glibc-2.9-2.i686 \n 42 | * 测试图示: 43 | * @image html pic-640x480.png "租房一角(640x480)" 44 | * @image html pic-1024x768.png "指定范围太大,失败(1024x768)" 45 | * 46 | */ 47 | 48 | #include 49 | #include "v4l2uvc.h" 50 | 51 | #ifdef CAPTURE_SDL 52 | 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | 59 | /** SDL 视频标志 */ 60 | static Uint32 SDL_VIDEO_Flags = 61 | SDL_ANYFORMAT | SDL_DOUBLEBUF | SDL_RESIZABLE; 62 | 63 | /** 摄像头信息结构体 */ 64 | struct video_info* vd_info = NULL; 65 | 66 | /** 67 | * sig_int - 信号处理函数 68 | * 69 | * @param signum 参数,可不用理会 70 | * @note 本程序中只处理Ctrl+c中断(SIGINT) 71 | */ 72 | static void sig_int(int signum) 73 | { 74 | debug_msg("\ncatch a SIGINT signal, you may be press Ctrl+C.\n"); 75 | vd_info->is_quit = 1; 76 | debug_msg("ready to quit\n"); 77 | 78 | } 79 | 80 | /** 81 | * get_info - 获取摄像头信息测试函数,由main函数调用 82 | * 83 | * 84 | * @return 成功返回0,否则返回-1,并提示出错信息 85 | */ 86 | static int get_info(void) 87 | { 88 | vd_info = (struct video_info *) calloc(1, sizeof(struct video_info)); 89 | if (v4l2_open(vd_info) <0) 90 | exit(1); 91 | if (v4l2_get_capability(vd_info) < 0) 92 | exit(1); 93 | if (v4l2_get_format(vd_info) < 0) 94 | exit(1); 95 | if (vd_info->is_streaming) /* stop if it is still capturing */ 96 | v4l2_off(vd_info); 97 | 98 | if (vd_info->frame_buffer) 99 | free(vd_info->frame_buffer); 100 | if (vd_info->tmp_buffer) 101 | free(vd_info->tmp_buffer); 102 | vd_info->frame_buffer = NULL; 103 | close(vd_info->camfd); 104 | 105 | free(vd_info); 106 | 107 | return 0; 108 | } 109 | 110 | /** 111 | * display - 使用SDL显示图像 112 | * 113 | * 114 | * @return 成功返回0,否则返回-1,并提示出错信息 115 | * @note 本函数涉及许多SDL库的类型及函数 116 | */ 117 | static int display(void) 118 | { 119 | 120 | SDL_Surface *pscreen = NULL; 121 | SDL_Overlay *overlay = NULL; 122 | SDL_Rect drect; 123 | SDL_Event sdlevent; 124 | SDL_mutex *affmutex = NULL; 125 | unsigned char frmrate; 126 | unsigned char *p = NULL; 127 | uint32 currtime; 128 | uint32 lasttime; 129 | char* status = NULL; 130 | 131 | /************* Test SDL capabilities ************/ 132 | /* memory leak here */ 133 | 134 | if (SDL_Init(SDL_INIT_VIDEO) < 0) 135 | { 136 | fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError()); 137 | exit(1); 138 | } 139 | 140 | /* it need to alloc space! */ 141 | vd_info = (struct video_info *) calloc(1, sizeof(struct video_info)); 142 | 143 | /* init the camera,you can change the last three params!!! */ 144 | if (v4l2_init(vd_info, V4L2_PIX_FMT_YUYV, 640,480) < 0) 145 | return EXIT_FAILURE; 146 | 147 | pscreen = 148 | SDL_SetVideoMode(vd_info->width, vd_info->height, 0, 149 | SDL_VIDEO_Flags); 150 | 151 | overlay = 152 | SDL_CreateYUVOverlay(vd_info->width, vd_info->height, 153 | SDL_YUY2_OVERLAY, pscreen); 154 | /* here?? */ 155 | p = (unsigned char *) overlay->pixels[0]; 156 | drect.x = 0; 157 | drect.y = 0; 158 | drect.w = pscreen->w; 159 | drect.h = pscreen->h; 160 | 161 | lasttime = SDL_GetTicks(); 162 | 163 | affmutex = SDL_CreateMutex(); 164 | /* big loop */ 165 | while (!vd_info->is_quit) 166 | { 167 | while (SDL_PollEvent(&sdlevent)) 168 | { 169 | if (sdlevent.type == SDL_QUIT) 170 | { 171 | vd_info->is_quit = 1; 172 | break; 173 | } 174 | } 175 | currtime = SDL_GetTicks(); 176 | if (currtime - lasttime > 0) 177 | { 178 | frmrate = 1000/(currtime - lasttime); 179 | } 180 | lasttime = currtime; 181 | 182 | if (v4l2_readframe(vd_info) < 0) 183 | { 184 | printf("Error grabbing \n"); 185 | break; 186 | } 187 | 188 | SDL_LockYUVOverlay(overlay); 189 | /* frame_buffer to p */ 190 | memcpy(p, vd_info->frame_buffer, 191 | vd_info->width * (vd_info->height) * 2); 192 | SDL_UnlockYUVOverlay(overlay); 193 | 194 | SDL_DisplayYUVOverlay(overlay, &drect); /* dispaly it */ 195 | status = (char *)calloc(1, 20*sizeof(char)); 196 | sprintf(status, "come on fps:%d",frmrate); 197 | SDL_WM_SetCaption(status, NULL); 198 | 199 | SDL_Delay(10); 200 | } 201 | 202 | SDL_DestroyMutex(affmutex); 203 | SDL_FreeYUVOverlay(overlay); 204 | 205 | v4l2_close(vd_info); 206 | free(status); 207 | free(vd_info); 208 | 209 | printf("Clean Up done Quit \n"); 210 | SDL_Quit(); 211 | return 0; 212 | } 213 | 214 | /** 215 | * capture_sdl - SDL捕获显示主函数 216 | * 217 | * @return 成功返回0,否则返回-1,并提示出错信息 218 | * @note 测试过程 \n 219 | * 1、先获取摄像头相关信息; 220 | * 2、等待用户输入回车, 221 | * 3、回车后,采集数据并显示,按Ctrl+c中断,或按SDL窗口关闭程序,否则一直显示 222 | */ 223 | int capture_sdl(int argc, char* argv[]) 224 | { 225 | signal(SIGINT, sig_int); 226 | get_info(); 227 | msg_out("I am ready!\n"); 228 | msg_out("press enter key to capture and display!\n"); 229 | getchar(); 230 | 231 | display(); 232 | return 0; 233 | } 234 | #else 235 | int capture_sdl(int argc, char* argv[]) 236 | { 237 | msg_out("sdl not available here, exiting..\n"); 238 | return 0; 239 | } 240 | #endif 241 | -------------------------------------------------------------------------------- /capture_simple.c: -------------------------------------------------------------------------------- 1 | /* 2 | * _/_/ _/_/_/ _/_/_/_/_/ 3 | * _/ _/ _/ _/ 4 | * _/ _/ _/ 5 | * _/ _/_/ _/ 6 | * _/ _/ _/ 7 | * _/ _/ _/ _/ _/ 8 | * _/_/ _/_/ _/_/_/ 9 | */ 10 | /** 11 | * Copyleft (C) 2010 Late Lee 12 | * This program is tested on LINUX PLATFORM, WITH GCC 4.x. 13 | * The program is distributed in the hope that it will be 14 | * useful, but WITHOUT ANY WARRANTY. Please feel free to 15 | * use the program, and I feel free to ignore the related 16 | * issues. Any questions or suggestions, or bugs, please 17 | * contact me at 18 | * <$ echo -n "aHR0cDovL3d3dy5sYXRlbGVlLm9yZwo=" | base64 -d> 19 | * or e-mail to 20 | * <$ echo -n "bGF0ZWxlZUAxNjMuY29tCg==" | base64 -d> 21 | * if you want to do this. 22 | * 23 | * @file capture_sdl.c 24 | * @author Late Lee 25 | * @date Sat May 29 2010 26 | * 27 | * @brief none 28 | * 29 | * @test 30 | * 在程序目录中直接输入\p make 即可编译生成\p capture 可执行文件。\n 31 | * 32 | */ 33 | 34 | #include 35 | #include "v4l2uvc.h" 36 | 37 | /** 摄像头信息结构体 */ 38 | struct video_info* vd_info = NULL; 39 | int fps = 0; 40 | int g_fmt = 0; 41 | int g_width = 0; 42 | int g_height = 0; 43 | 44 | FILE* fp = NULL; 45 | char file_name[16] = {0}; // = "raw.yuv"; 46 | 47 | enum v4l2_driver_type g_driver_type = V4L2_DRIVER_UNKNOWN; 48 | /** 49 | * sig_int - 信号处理函数 50 | * 51 | * @param signum 参数,可不用理会 52 | * @note 本程序中只处理Ctrl+c中断(SIGINT) 53 | */ 54 | static void sig_int(int signum) 55 | { 56 | debug_msg("\ncatch a SIGINT signal, you may be press Ctrl+C.\n"); 57 | debug_msg("ready to quit\n"); 58 | if (vd_info == NULL) 59 | goto end; 60 | 61 | vd_info->is_quit = 1; 62 | // stop.... 63 | v4l2_close(vd_info); 64 | 65 | end: 66 | if (fp) 67 | { 68 | fclose(fp); 69 | fp = NULL; 70 | } 71 | exit(0); 72 | } 73 | 74 | static void sig_alarm(int signum) 75 | { 76 | printf("fps=%dfps\n",fps); 77 | fps = 0; 78 | 79 | alarm(1); 80 | } 81 | 82 | static int my_v4l2_process(struct video_info* vd_info) 83 | { 84 | //debug_msg("my process....\n"); 85 | 86 | 87 | //v4l2_get_pic(vd_info); 88 | 89 | return 0; 90 | 91 | 92 | static int init = 0; 93 | static int cnt = 0; 94 | 95 | // 保存为yuv格式文件 96 | if (vd_info->format != V4L2_PIX_FMT_MJPEG) 97 | { 98 | strcpy(file_name, "raw.yuv"); 99 | if (init == 0) 100 | { 101 | fp = fopen(file_name, "w"); 102 | init = 1; 103 | if (NULL == fp) 104 | unix_error_ret("unable to open the file"); 105 | } 106 | 107 | fwrite(vd_info->frame_buffer, 1, vd_info->frame_size_in, fp); 108 | debug_msg("writing %d...\n", cnt); 109 | } 110 | // JPEG 111 | else 112 | { 113 | sprintf(file_name, "%d.jpg", cnt++); 114 | 115 | fp = fopen(file_name, "w"); 116 | if (NULL == fp) 117 | unix_error_ret("unable to open the file"); 118 | 119 | fwrite(vd_info->tmp_buffer, 1, vd_info->buf.bytesused, fp); 120 | debug_msg("writing %d...\n", cnt); 121 | 122 | if (fp) 123 | { 124 | fclose(fp); 125 | fp = NULL; 126 | } 127 | } 128 | 129 | 130 | return 0; 131 | } 132 | 133 | /** 134 | * get_info - 获取摄像头信息测试函数,由main函数调用 135 | * 136 | * 137 | * @return 成功返回0,否则返回-1,并提示出错信息 138 | */ 139 | static int get_info(char* videodevice) 140 | { 141 | struct video_info* vd_info = (struct video_info *) calloc(1, sizeof(struct video_info)); 142 | strcpy(vd_info->name, videodevice); 143 | 144 | if (v4l2_open(vd_info) <0) 145 | exit(1); 146 | if (v4l2_get_capability(vd_info) < 0) 147 | exit(1); 148 | 149 | if (v4l2_enum_format(vd_info) < 0) 150 | exit(1); 151 | if (v4l2_get_format(vd_info) < 0) 152 | exit(1); 153 | if (vd_info->is_streaming) /* stop if it is still capturing */ 154 | v4l2_off(vd_info); 155 | 156 | g_fmt = vd_info->format; 157 | g_width = vd_info->width; 158 | g_height = vd_info->height; 159 | g_driver_type = vd_info->driver_type; 160 | 161 | 162 | if (vd_info->frame_buffer) 163 | free(vd_info->frame_buffer); 164 | if (vd_info->tmp_buffer) 165 | free(vd_info->tmp_buffer); 166 | vd_info->frame_buffer = NULL; 167 | 168 | v4l2_close(vd_info); 169 | 170 | free(vd_info); 171 | 172 | return 0; 173 | } 174 | 175 | /** 176 | * capture_v4l2simple - 最简单的测试函数 177 | * 178 | * @return 成功返回0,否则返回-1,并提示出错信息 179 | * @note 180 | * 1、先获取摄像头相关信息; 181 | * 182 | */ 183 | int capture_v4l2simple(int argc, char* argv[]) 184 | { 185 | char videodevice[16] = {0}; 186 | strcpy(videodevice, "/dev/video0"); 187 | if (argc == 2) 188 | { 189 | strcpy(videodevice, argv[1]); 190 | } 191 | printf("willl open %s\n", videodevice); 192 | signal(SIGINT, sig_int); 193 | 194 | get_info(videodevice); 195 | 196 | // 在按回车之前捕获摄像头信息,之后继续后面的事 197 | msg_out("press enter key to capture !\n"); 198 | getchar(); 199 | 200 | signal(SIGALRM, sig_alarm); 201 | alarm(1); 202 | 203 | vd_info = (struct video_info *) calloc(1, sizeof(struct video_info)); 204 | 205 | strcpy(vd_info->name, videodevice); 206 | 207 | vd_info->format = V4L2_PIX_FMT_YUYV; //g_fmt; //g_fmt; //V4L2_PIX_FMT_NV21;//V4L2_PIX_FMT_MJPEG; //V4L2_PIX_FMT_NV12; 208 | vd_info->width = 640; //g_width; // 640 209 | vd_info->height = 480;//g_height; //480; 210 | vd_info->driver_type = g_driver_type; 211 | 212 | 213 | printf("====info video fmt: 0x%x res: %dx%d driver type: %d\n", 214 | vd_info->format, vd_info->width, vd_info->height, vd_info->driver_type); 215 | 216 | v4l2_set_processcb(my_v4l2_process); 217 | 218 | if (v4l2_init(vd_info) < 0) 219 | return -1; 220 | 221 | while (1) 222 | { 223 | if (v4l2_readframe(vd_info) < 0) 224 | { 225 | printf("Error grabbing \n"); 226 | //break; 227 | } 228 | 229 | fps++; 230 | } 231 | 232 | v4l2_close(vd_info); 233 | 234 | free(vd_info); 235 | 236 | return 0; 237 | } -------------------------------------------------------------------------------- /color.c: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | # GspcaGui: Gspca/Spca5xx Grabber # 3 | # Copyright (C) 2004 2005 2006 Michel Xhaard # 4 | # # 5 | # This program is free software; you can redistribute it and/or modify # 6 | # it under the terms of the GNU General Public License as published by # 7 | # the Free Software Foundation; either version 2 of the License, or # 8 | # (at your option) any later version. # 9 | # # 10 | # This program is distributed in the hope that it will be useful, # 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # 13 | # GNU General Public License for more details. # 14 | # # 15 | # You should have received a copy of the GNU General Public License # 16 | # along with this program; if not, write to the Free Software # 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # 18 | # # 19 | ****************************************************************************/ 20 | #include 21 | #include 22 | #include 23 | #include "color.h" 24 | 25 | static int *LutYr = NULL; 26 | static int *LutYg = NULL;; 27 | static int *LutYb = NULL;; 28 | static int *LutVr = NULL;; 29 | static int *LutVrY = NULL;; 30 | static int *LutUb = NULL;; 31 | static int *LutUbY = NULL;; 32 | static int *LutRv = NULL; 33 | static int *LutGu = NULL; 34 | static int *LutGv = NULL; 35 | static int *LutBu = NULL; 36 | 37 | #if 0 38 | #define RGB24_TO_Y(r,g,b) LutYr[(r)] + LutYg[(g)] + LutYb[(b)] 39 | #define YR_TO_V(r,y) LutVr[(r)] + LutVrY[(y)] 40 | #define YB_TO_U(b,y) LutUb[(b)] + LutUbY[(y)] 41 | 42 | #define R_FROMYV(y,v) CLIP((y) + LutRv[(v)]) 43 | #define G_FROMYUV(y,u,v) CLIP((y) + LutGu[(u)] + LutGv[(v)]) 44 | #define B_FROMYU(y,u) CLIP((y) + LutBu[(u)]) 45 | #endif 46 | 47 | unsigned char 48 | RGB24_TO_Y(unsigned char r, unsigned char g, unsigned char b) 49 | { 50 | return (LutYr[(r)] + LutYg[(g)] + LutYb[(b)]); 51 | } 52 | unsigned char 53 | YR_TO_V(unsigned char r, unsigned char y) 54 | { 55 | return (LutVr[(r)] + LutVrY[(y)]); 56 | } 57 | unsigned char 58 | YB_TO_U(unsigned char b, unsigned char y) 59 | { 60 | return (LutUb[(b)] + LutUbY[(y)]); 61 | } 62 | unsigned char 63 | R_FROMYV(unsigned char y, unsigned char v) 64 | { 65 | return CLIP((y) + LutRv[(v)]); 66 | } 67 | unsigned char 68 | G_FROMYUV(unsigned char y, unsigned char u, unsigned char v) 69 | { 70 | return CLIP((y) + LutGu[(u)] + LutGv[(v)]); 71 | } 72 | unsigned char 73 | B_FROMYU(unsigned char y, unsigned char u) 74 | { 75 | return CLIP((y) + LutBu[(u)]); 76 | } 77 | 78 | void initLut(void) 79 | { 80 | int i; 81 | #define Rcoef 299 82 | #define Gcoef 587 83 | #define Bcoef 114 84 | #define Vrcoef 711 //656 //877 85 | #define Ubcoef 560 //500 //493 564 86 | 87 | #define CoefRv 1402 88 | #define CoefGu 714 // 344 89 | #define CoefGv 344 // 714 90 | #define CoefBu 1772 91 | 92 | LutYr = malloc(256*sizeof(int)); 93 | LutYg = malloc(256*sizeof(int)); 94 | LutYb = malloc(256*sizeof(int)); 95 | LutVr = malloc(256*sizeof(int)); 96 | LutVrY = malloc(256*sizeof(int)); 97 | LutUb = malloc(256*sizeof(int)); 98 | LutUbY = malloc(256*sizeof(int)); 99 | 100 | LutRv = malloc(256*sizeof(int)); 101 | LutGu = malloc(256*sizeof(int)); 102 | LutGv = malloc(256*sizeof(int)); 103 | LutBu = malloc(256*sizeof(int)); 104 | for (i= 0;i < 256;i++){ 105 | LutYr[i] = i*Rcoef/1000 ; 106 | LutYg[i] = i*Gcoef/1000 ; 107 | LutYb[i] = i*Bcoef/1000 ; 108 | LutVr[i] = i*Vrcoef/1000; 109 | LutUb[i] = i*Ubcoef/1000; 110 | LutVrY[i] = 128 -(i*Vrcoef/1000); 111 | LutUbY[i] = 128 -(i*Ubcoef/1000); 112 | LutRv[i] = (i-128)*CoefRv/1000; 113 | LutBu[i] = (i-128)*CoefBu/1000; 114 | LutGu[i] = (128-i)*CoefGu/1000; 115 | LutGv[i] = (128-i)*CoefGv/1000; 116 | } 117 | } 118 | 119 | 120 | void freeLut(void){ 121 | free(LutYr); 122 | free(LutYg); 123 | free(LutYb); 124 | free(LutVr); 125 | free(LutVrY); 126 | free(LutUb); 127 | free(LutUbY); 128 | 129 | free(LutRv); 130 | free(LutGu); 131 | free(LutGv); 132 | free(LutBu); 133 | } 134 | 135 | -------------------------------------------------------------------------------- /color.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | # GspcaGui: Gspca/Spca5xx Grabber # 3 | # Copyright (C) 2004 2005 2006 Michel Xhaard # 4 | # # 5 | # This program is free software; you can redistribute it and/or modify # 6 | # it under the terms of the GNU General Public License as published by # 7 | # the Free Software Foundation; either version 2 of the License, or # 8 | # (at your option) any later version. # 9 | # # 10 | # This program is distributed in the hope that it will be useful, # 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # 13 | # GNU General Public License for more details. # 14 | # # 15 | # You should have received a copy of the GNU General Public License # 16 | # along with this program; if not, write to the Free Software # 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # 18 | # # 19 | ****************************************************************************/ 20 | typedef struct Myrgb16 { 21 | unsigned short blue:5; 22 | unsigned short green:6; 23 | unsigned short red:5; 24 | } Myrgb16; 25 | typedef struct Myrgb24 { 26 | unsigned char blue; 27 | unsigned char green; 28 | unsigned char red; 29 | } Myrgb24; 30 | typedef struct Myrgb32 { 31 | unsigned char blue; 32 | unsigned char green; 33 | unsigned char red; 34 | unsigned char alpha; 35 | } Myrgb32; 36 | 37 | typedef struct MyYUV422 { 38 | unsigned char y0; 39 | unsigned char u; 40 | unsigned char y1; 41 | unsigned char v; 42 | } MyYUV422; 43 | 44 | typedef struct MyYUV444 { 45 | unsigned char y; 46 | unsigned char u; 47 | unsigned char v; 48 | } MyYUV444; 49 | 50 | #define CLIP(color) (unsigned char)(((color)>0xFF)?0xff:(((color)<0)?0:(color))) 51 | 52 | unsigned char 53 | RGB24_TO_Y(unsigned char r, unsigned char g, unsigned char b); 54 | 55 | unsigned char 56 | YR_TO_V(unsigned char r, unsigned char y); 57 | 58 | unsigned char 59 | YB_TO_U(unsigned char b, unsigned char y); 60 | 61 | unsigned char 62 | R_FROMYV(unsigned char y, unsigned char v); 63 | 64 | unsigned char 65 | G_FROMYUV(unsigned char y, unsigned char u, unsigned char v); 66 | 67 | unsigned char 68 | B_FROMYU(unsigned char y, unsigned char u); 69 | 70 | #define YfromRGB(r,g,b) CLIP((77*(r)+150*(g)+29*(b))>>8) 71 | #define UfromRGB(r,g,b) CLIP(((128*(b)-85*(g)-43*(r))>>8 )+128) 72 | #define VfromRGB(r,g,b) CLIP(((128*(r)-107*(g)-21*(b))>>8) +128) 73 | 74 | #define PACKRGB16(r,g,b) (__u16) ((((b) & 0xF8) << 8 ) | (((g) & 0xFC) << 3 ) | (((r) & 0xF8) >> 3 )) 75 | #define UNPACK16(pixel,r,g,b) r=((pixel)&0xf800) >> 8; g=((pixel)&0x07e0) >> 3; b=(((pixel)&0x001f) << 3) 76 | 77 | void initLut(void); 78 | void freeLut(void); 79 | -------------------------------------------------------------------------------- /debug-msg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * _/_/ _/_/_/ _/_/_/_/_/ 3 | * _/ _/ _/ _/ 4 | * _/ _/ _/ 5 | * _/ _/_/ _/ 6 | * _/ _/ _/ 7 | * _/ _/ _/ _/ _/ 8 | * _/_/ _/_/ _/_/_/ 9 | */ 10 | /** 11 | * Copyleft (C) 2010 Late Lee 12 | * This program is tested on LINUX PLATFORM, WITH GCC 4.x. 13 | * The program is distributed in the hope that it will be 14 | * useful, but WITHOUT ANY WARRANTY. Please feel free to 15 | * use the program, and I feel free to ignore the related 16 | * issues. Any questions or suggestions, or bugs, please 17 | * contact me at 18 | * <$ echo -n "aHR0cDovL3d3dy5sYXRlbGVlLm9yZwo=" | base64 -d> 19 | * or e-mail to 20 | * <$ echo -n "bGF0ZWxlZUAxNjMuY29tCg==" | base64 -d> 21 | * if you want to do this. 22 | * 23 | * @file debug-msg.h 24 | * @author Late Lee 25 | * @date 2010 26 | * @brief Some macros(debug, error handle) usually used in my programs. 27 | * 28 | * @test I only use this file on linux(with x86 & arm) platform. 29 | * @note You must \a define DEBUG before you include this file: 30 | * #define DEBUG 31 | * #include "debug-msg.h" 32 | */ 33 | 34 | #ifndef _DEBUG_MSG_H 35 | #define _DEBUG_MSG_H 36 | 37 | #include /* perror() */ 38 | #include /* strerror() */ 39 | #include /* errno */ 40 | 41 | /*** 42 | * Print the error msg is one thing 43 | * but print the msg is another!! 44 | */ 45 | 46 | /** 47 | * @def error_exit 48 | * @brief A macro that prints the @a error msg and exit. 49 | */ 50 | 51 | #define error_exit(error) \ 52 | do{ \ 53 | fprintf(stderr, "%s\n", error); \ 54 | exit(0); \ 55 | } while(0) 56 | 57 | /** 58 | * @def error_ret 59 | * @brief A macro that prints the @a error msg and return -1. 60 | */ 61 | #define error_ret(error) \ 62 | do{ \ 63 | fprintf(stderr, "%s\n", error); \ 64 | return -1; \ 65 | } while(0) 66 | 67 | /** 68 | * @def unix_error_exit 69 | * @brief A macro that prints the @a error msg(with errno) and then exit. 70 | * I put 'unix' before the 'function' name because I am using 'errno'. 71 | */ 72 | #define unix_error_exit(error) \ 73 | do{ \ 74 | fprintf(stderr, "%s Info[%d]:%s\n", \ 75 | error, errno, strerror(errno)); \ 76 | exit(1); \ 77 | } while(0) 78 | 79 | /** 80 | * @def unix_error_ret 81 | * @brief A macro that prints the @a error msg(with errno) and then return -1. 82 | * I put 'unix' before the 'function' name because I am using 'errno'. 83 | */ 84 | #define unix_error_ret(error) \ 85 | do{ \ 86 | fprintf(stderr, "%s Info[%d]:%s\n", \ 87 | error, errno, strerror(errno)); \ 88 | return -1; \ 89 | } while(0) 90 | 91 | /* error handle ,detail version */ 92 | /* it can show the file,function and the line */ 93 | /** 94 | * @def unix_print_error 95 | * @brief A macro that prints the @a error msg(with errno) and then exit. 96 | * I put 'unix' before the 'function' name because I am using 'errno'.@n 97 | * This error handle(detail version) can show the @p file, @p function 98 | * and @p line where the @a error happens. 99 | * @note I do not often use this 'function' in my program. 100 | */ 101 | #define unix_print_error(error) \ 102 | do { \ 103 | fprintf(stderr, "Oh God!\nFile:%s Line:%d Function:%s:\n", \ 104 | __FILE__, __LINE__, __func__); \ 105 | perror(error); \ 106 | exit(0); \ 107 | } while(0) 108 | 109 | /** 110 | * @def debug_msg 111 | * @brief A macro that prints the debug msg, as the same with @a printf. 112 | * 113 | */ 114 | #ifdef DEBUG 115 | #define debug_msg(fmt, ...) \ 116 | fprintf(stdout, fmt, ##__VA_ARGS__) 117 | #else 118 | #define debug_msg(fmt,...) 119 | #endif /* DEBUG */ 120 | 121 | #define msg_out(format,...) \ 122 | fprintf(stdout,format,##__VA_ARGS__) 123 | 124 | #ifndef TRACE 125 | #define TRACE 1 /**< we trace all the time */ 126 | #endif 127 | 128 | /** 129 | * @def debug_trace 130 | * @brief A macro that traces the @p file, @p function and @p line. 131 | * 132 | */ 133 | #if TRACE > 0 134 | #define debug_trace(trace) \ 135 | fprintf(stdout, "%s File:%s Line:%d Func:%s.\n", \ 136 | trace, __FILE__, __LINE__, __func__) 137 | #else 138 | #define debug_trace(trace) 139 | #endif 140 | 141 | #endif /* _DEBUG_MSG_H */ 142 | -------------------------------------------------------------------------------- /fb_utils.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/latelee/CameraDemo_Linux/5e1f696ea42150ddc16fa90a4e97a6d81f8e79bc/fb_utils.c -------------------------------------------------------------------------------- /fb_utils.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/latelee/CameraDemo_Linux/5e1f696ea42150ddc16fa90a4e97a6d81f8e79bc/fb_utils.h -------------------------------------------------------------------------------- /huffman.h: -------------------------------------------------------------------------------- 1 | 2 | #define DHT_SIZE 432 3 | 4 | static unsigned char dht_data[DHT_SIZE] = { 5 | 0xff, 0xc4, 0x00, 0x1f, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 6 | 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 7 | 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 8 | 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, 0x02, 9 | 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 10 | 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 11 | 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 12 | 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 13 | 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 14 | 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 15 | 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 16 | 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 17 | 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 18 | 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 19 | 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 20 | 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 21 | 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 22 | 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 23 | 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 24 | 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 25 | 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 26 | 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc4, 0x00, 0x1f, 27 | 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 28 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 29 | 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xff, 0xc4, 0x00, 30 | 0xb5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 31 | 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 32 | 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 33 | 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 34 | 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 35 | 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 36 | 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 37 | 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 38 | 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 39 | 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 40 | 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 41 | 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 42 | 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 43 | 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 44 | 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 45 | 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 46 | 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 47 | 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa 48 | }; 49 | 50 | #define JPG_HUFFMAN_TABLE_LENGTH 0x1A0 51 | 52 | const unsigned char JPEGHuffmanTable[JPG_HUFFMAN_TABLE_LENGTH] 53 | = { 54 | 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 55 | 0x00, 0x00, 0x00, 0x00, 0x00, 56 | 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 57 | 0x0A, 0x0B, 0x01, 0x00, 0x03, 58 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 59 | 0x00, 0x00, 0x00, 0x00, 0x01, 60 | 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 61 | 0x00, 0x02, 0x01, 0x03, 0x03, 62 | 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 63 | 0x01, 0x02, 0x03, 0x00, 0x04, 64 | 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 65 | 0x22, 0x71, 0x14, 0x32, 0x81, 66 | 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 67 | 0x24, 0x33, 0x62, 0x72, 0x82, 68 | 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 69 | 0x29, 0x2A, 0x34, 0x35, 0x36, 70 | 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 71 | 0x4A, 0x53, 0x54, 0x55, 0x56, 72 | 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 73 | 0x6A, 0x73, 0x74, 0x75, 0x76, 74 | 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 75 | 0x8A, 0x92, 0x93, 0x94, 0x95, 76 | 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 77 | 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 78 | 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 79 | 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 80 | 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 81 | 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 82 | 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 83 | 0xF9, 0xFA, 0x11, 0x00, 0x02, 84 | 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 85 | 0x01, 0x02, 0x77, 0x00, 0x01, 86 | 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 87 | 0x07, 0x61, 0x71, 0x13, 0x22, 88 | 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 89 | 0x33, 0x52, 0xF0, 0x15, 0x62, 90 | 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 91 | 0x19, 0x1A, 0x26, 0x27, 0x28, 92 | 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 93 | 0x46, 0x47, 0x48, 0x49, 0x4A, 94 | 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 95 | 0x66, 0x67, 0x68, 0x69, 0x6A, 96 | 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 97 | 0x85, 0x86, 0x87, 0x88, 0x89, 98 | 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 99 | 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 100 | 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 101 | 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 102 | 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 103 | 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 104 | 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 105 | 0xF6, 0xF7, 0xF8, 0xF9, 0xFA 106 | }; 107 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * _/_/ _/_/_/ _/_/_/_/_/ 3 | * _/ _/ _/ _/ 4 | * _/ _/ _/ 5 | * _/ _/_/ _/ 6 | * _/ _/ _/ 7 | * _/ _/ _/ _/ _/ 8 | * _/_/ _/_/ _/_/_/ 9 | */ 10 | /** 11 | * Copyleft (C) 2010 Late Lee 12 | * This program is tested on LINUX PLATFORM, WITH GCC 4.x. 13 | * The program is distributed in the hope that it will be 14 | * useful, but WITHOUT ANY WARRANTY. Please feel free to 15 | * use the program, and I feel free to ignore the related 16 | * issues. Any questions or suggestions, or bugs, please 17 | * contact me at 18 | * <$ echo -n "aHR0cDovL3d3dy5sYXRlbGVlLm9yZwo=" | base64 -d> 19 | * or e-mail to 20 | * <$ echo -n "bGF0ZWxlZUAxNjMuY29tCg==" | base64 -d> 21 | * if you want to do this. 22 | * 23 | * @file main.c 24 | * @author Late Lee 25 | * @date Sat May 29 2010 26 | * 27 | * @brief none 28 | * 29 | * 30 | */ 31 | 32 | 33 | #include "capture.h" 34 | 35 | /** 36 | * main - 主函数 37 | * 38 | */ 39 | int main(int argc, char* argv[]) 40 | { 41 | //capture_v4l2simple(argc, argv); 42 | capture_fb(argc, argv); 43 | //capture_sdl(argc, argv); 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /my_types.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/latelee/CameraDemo_Linux/5e1f696ea42150ddc16fa90a4e97a6d81f8e79bc/my_types.h -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | # luvcview: Sdl video Usb Video Class grabber . # 3 | #This package work with the Logitech UVC based webcams with the mjpeg feature. # 4 | #All the decoding is in user space with the embedded jpeg decoder # 5 | #. # 6 | # Copyright (C) 2005 2006 Laurent Pinchart && Michel Xhaard # 7 | # # 8 | # This program is free software; you can redistribute it and/or modify # 9 | # it under the terms of the GNU General Public License as published by # 10 | # the Free Software Foundation; either version 2 of the License, or # 11 | # (at your option) any later version. # 12 | # # 13 | # This program is distributed in the hope that it will be useful, # 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # 16 | # GNU General Public License for more details. # 17 | # # 18 | # You should have received a copy of the GNU General Public License # 19 | # along with this program; if not, write to the Free Software # 20 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # 21 | # # 22 | *******************************************************************************/ 23 | 24 | #include "utils.h" 25 | #include "color.h" 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "huffman.h" 36 | 37 | #define ISHIFT 11 38 | 39 | #define IFIX(a) ((int)((a) * (1 << ISHIFT) + .5)) 40 | 41 | #ifndef __P 42 | # define __P(x) x 43 | #endif 44 | 45 | /* special markers */ 46 | #define M_BADHUFF -1 47 | #define M_EOF 0x80 48 | 49 | struct jpeg_decdata { 50 | int dcts[6 * 64 + 16]; 51 | int out[64 * 6]; 52 | int dquant[3][64]; 53 | }; 54 | 55 | struct in { 56 | unsigned char *p; 57 | unsigned int bits; 58 | int left; 59 | int marker; 60 | int (*func) __P((void *)); 61 | void *data; 62 | }; 63 | 64 | /*********************************/ 65 | struct dec_hufftbl; 66 | struct enc_hufftbl; 67 | 68 | union hufftblp { 69 | struct dec_hufftbl *dhuff; 70 | struct enc_hufftbl *ehuff; 71 | }; 72 | 73 | struct scan { 74 | int dc; /* old dc value */ 75 | 76 | union hufftblp hudc; 77 | union hufftblp huac; 78 | int next; /* when to switch to next scan */ 79 | 80 | int cid; /* component id */ 81 | int hv; /* horiz/vert, copied from comp */ 82 | int tq; /* quant tbl, copied from comp */ 83 | }; 84 | 85 | /*********************************/ 86 | 87 | #define DECBITS 10 /* seems to be the optimum */ 88 | 89 | struct dec_hufftbl { 90 | int maxcode[17]; 91 | int valptr[16]; 92 | unsigned char vals[256]; 93 | unsigned int llvals[1 << DECBITS]; 94 | }; 95 | static int huffman_init(void); 96 | static void decode_mcus 97 | __P((struct in *, int *, int, struct scan *, int *)); 98 | static int dec_readmarker __P((struct in *)); 99 | static void dec_makehuff 100 | __P((struct dec_hufftbl *, int *, unsigned char *)); 101 | 102 | static void setinput __P((struct in *, unsigned char *)); 103 | /*********************************/ 104 | 105 | #undef PREC 106 | #define PREC int 107 | 108 | static void idctqtab __P((unsigned char *, PREC *)); 109 | 110 | inline static void idct(int *in, int *out, int *quant, long off, int max); 111 | 112 | int is_huffman(unsigned char *buf); 113 | 114 | /*********************************/ 115 | 116 | static void yuv420pto422(int * out,unsigned char *pic,int width); 117 | static void yuv422pto422(int * out,unsigned char *pic,int width); 118 | static void yuv444pto422(int * out,unsigned char *pic,int width); 119 | static void yuv400pto422(int * out,unsigned char *pic,int width); 120 | typedef void (*ftopict) ( int *out, unsigned char *pic, int width) ; 121 | /*********************************/ 122 | 123 | #define M_SOI 0xd8 124 | #define M_APP0 0xe0 125 | #define M_DQT 0xdb 126 | #define M_SOF0 0xc0 127 | #define M_DHT 0xc4 128 | #define M_DRI 0xdd 129 | #define M_SOS 0xda 130 | #define M_RST0 0xd0 131 | #define M_EOI 0xd9 132 | #define M_COM 0xfe 133 | 134 | static unsigned char *datap; 135 | 136 | static int getbyte(void) 137 | { 138 | return *datap++; 139 | } 140 | 141 | static int getword(void) 142 | { 143 | int c1, c2; 144 | c1 = *datap++; 145 | c2 = *datap++; 146 | return c1 << 8 | c2; 147 | } 148 | 149 | struct comp { 150 | int cid; 151 | int hv; 152 | int tq; 153 | }; 154 | 155 | #define MAXCOMP 4 156 | struct jpginfo { 157 | int nc; /* number of components */ 158 | int ns; /* number of scans */ 159 | int dri; /* restart interval */ 160 | int nm; /* mcus til next marker */ 161 | int rm; /* next restart marker */ 162 | }; 163 | 164 | static struct jpginfo info; 165 | static struct comp comps[MAXCOMP]; 166 | 167 | static struct scan dscans[MAXCOMP]; 168 | 169 | static unsigned char quant[4][64]; 170 | 171 | static struct dec_hufftbl dhuff[4]; 172 | 173 | #define dec_huffdc (dhuff + 0) 174 | #define dec_huffac (dhuff + 2) 175 | 176 | static struct in in; 177 | 178 | static int readtables(int till, int *isDHT) 179 | { 180 | int m, l, i, j, lq, pq, tq; 181 | int tc, th, tt; 182 | 183 | for (;;) { 184 | if (getbyte() != 0xff) 185 | return -1; 186 | if ((m = getbyte()) == till) 187 | break; 188 | 189 | switch (m) { 190 | case 0xc2: 191 | return 0; 192 | 193 | case M_DQT: 194 | //printf("find DQT \n"); 195 | lq = getword(); 196 | while (lq > 2) { 197 | pq = getbyte(); 198 | tq = pq & 15; 199 | if (tq > 3) 200 | return -1; 201 | pq >>= 4; 202 | if (pq != 0) 203 | return -1; 204 | for (i = 0; i < 64; i++) 205 | quant[tq][i] = getbyte(); 206 | lq -= 64 + 1; 207 | } 208 | break; 209 | 210 | case M_DHT: 211 | //printf("find DHT \n"); 212 | l = getword(); 213 | while (l > 2) { 214 | int hufflen[16], k; 215 | unsigned char huffvals[256]; 216 | 217 | tc = getbyte(); 218 | th = tc & 15; 219 | tc >>= 4; 220 | tt = tc * 2 + th; 221 | if (tc > 1 || th > 1) 222 | return -1; 223 | for (i = 0; i < 16; i++) 224 | hufflen[i] = getbyte(); 225 | l -= 1 + 16; 226 | k = 0; 227 | for (i = 0; i < 16; i++) { 228 | for (j = 0; j < hufflen[i]; j++) 229 | huffvals[k++] = getbyte(); 230 | l -= hufflen[i]; 231 | } 232 | dec_makehuff(dhuff + tt, hufflen, huffvals); 233 | } 234 | *isDHT= 1; 235 | break; 236 | 237 | case M_DRI: 238 | //printf("find DRI \n"); 239 | l = getword(); 240 | info.dri = getword(); 241 | break; 242 | 243 | default: 244 | l = getword(); 245 | while (l-- > 2) 246 | getbyte(); 247 | break; 248 | } 249 | } 250 | 251 | return 0; 252 | } 253 | 254 | static void dec_initscans(void) 255 | { 256 | int i; 257 | 258 | info.nm = info.dri + 1; 259 | info.rm = M_RST0; 260 | for (i = 0; i < info.ns; i++) 261 | dscans[i].dc = 0; 262 | } 263 | 264 | static int dec_checkmarker(void) 265 | { 266 | int i; 267 | 268 | if (dec_readmarker(&in) != info.rm) 269 | return -1; 270 | info.nm = info.dri; 271 | info.rm = (info.rm + 1) & ~0x08; 272 | for (i = 0; i < info.ns; i++) 273 | dscans[i].dc = 0; 274 | return 0; 275 | } 276 | 277 | 278 | int jpeg_decode(unsigned char **pic, unsigned char *buf, int *width, 279 | int *height) 280 | { 281 | struct jpeg_decdata *decdata; 282 | int i, j, m, tac, tdc; 283 | int intwidth, intheight; 284 | int mcusx, mcusy, mx, my; 285 | int ypitch ,xpitch,bpp,pitch,x,y; 286 | int mb; 287 | int max[6]; 288 | ftopict convert; 289 | int err = 0; 290 | int isInitHuffman = 0; 291 | decdata = (struct jpeg_decdata *) malloc(sizeof(struct jpeg_decdata)); 292 | 293 | if (!decdata) { 294 | err = -1; 295 | goto error; 296 | } 297 | if (buf == NULL) { 298 | err = -1; 299 | goto error; 300 | } 301 | datap = buf; 302 | if (getbyte() != 0xff) { 303 | err = ERR_NO_SOI; 304 | goto error; 305 | } 306 | if (getbyte() != M_SOI) { 307 | err = ERR_NO_SOI; 308 | goto error; 309 | } 310 | if (readtables(M_SOF0, &isInitHuffman)) { 311 | err = ERR_BAD_TABLES; 312 | goto error; 313 | } 314 | getword(); 315 | i = getbyte(); 316 | if (i != 8) { 317 | err = ERR_NOT_8BIT; 318 | goto error; 319 | } 320 | intheight = getword(); 321 | intwidth = getword(); 322 | 323 | if ((intheight & 7) || (intwidth & 7)) { 324 | err = ERR_BAD_WIDTH_OR_HEIGHT; 325 | goto error; 326 | } 327 | info.nc = getbyte(); 328 | if (info.nc > MAXCOMP) { 329 | err = ERR_TOO_MANY_COMPPS; 330 | goto error; 331 | } 332 | for (i = 0; i < info.nc; i++) { 333 | int h, v; 334 | comps[i].cid = getbyte(); 335 | comps[i].hv = getbyte(); 336 | v = comps[i].hv & 15; 337 | h = comps[i].hv >> 4; 338 | comps[i].tq = getbyte(); 339 | if (h > 3 || v > 3) { 340 | err = ERR_ILLEGAL_HV; 341 | goto error; 342 | } 343 | if (comps[i].tq > 3) { 344 | err = ERR_QUANT_TABLE_SELECTOR; 345 | goto error; 346 | } 347 | } 348 | if (readtables(M_SOS,&isInitHuffman)) { 349 | err = ERR_BAD_TABLES; 350 | goto error; 351 | } 352 | getword(); 353 | info.ns = getbyte(); 354 | if (!info.ns){ 355 | printf("info ns %d/n",info.ns); 356 | err = ERR_NOT_YCBCR_221111; 357 | goto error; 358 | } 359 | for (i = 0; i < info.ns; i++) { 360 | dscans[i].cid = getbyte(); 361 | tdc = getbyte(); 362 | tac = tdc & 15; 363 | tdc >>= 4; 364 | if (tdc > 1 || tac > 1) { 365 | err = ERR_QUANT_TABLE_SELECTOR; 366 | goto error; 367 | } 368 | for (j = 0; j < info.nc; j++) 369 | if (comps[j].cid == dscans[i].cid) 370 | break; 371 | if (j == info.nc) { 372 | err = ERR_UNKNOWN_CID_IN_SCAN; 373 | goto error; 374 | } 375 | dscans[i].hv = comps[j].hv; 376 | dscans[i].tq = comps[j].tq; 377 | dscans[i].hudc.dhuff = dec_huffdc + tdc; 378 | dscans[i].huac.dhuff = dec_huffac + tac; 379 | } 380 | 381 | i = getbyte(); 382 | j = getbyte(); 383 | m = getbyte(); 384 | 385 | if (i != 0 || j != 63 || m != 0) { 386 | printf("hmm FW error,not seq DCT ??\n"); 387 | } 388 | // printf("ext huffman table %d \n",isInitHuffman); 389 | if(!isInitHuffman) { 390 | if(huffman_init() < 0) 391 | return -ERR_BAD_TABLES; 392 | } 393 | /* 394 | if (dscans[0].cid != 1 || dscans[1].cid != 2 || dscans[2].cid != 3) { 395 | err = ERR_NOT_YCBCR_221111; 396 | goto error; 397 | } 398 | 399 | if (dscans[1].hv != 0x11 || dscans[2].hv != 0x11) { 400 | err = ERR_NOT_YCBCR_221111; 401 | goto error; 402 | } 403 | */ 404 | /* if internal width and external are not the same or heigth too 405 | and pic not allocated realloc the good size and mark the change 406 | need 1 macroblock line more ?? */ 407 | if (intwidth != *width || intheight != *height || *pic == NULL) { 408 | *width = intwidth; 409 | *height = intheight; 410 | // BytesperPixel 2 yuyv , 3 rgb24 411 | *pic = 412 | (unsigned char *) realloc((unsigned char *) *pic, 413 | (size_t) intwidth * (intheight + 414 | 8) * 2); 415 | } 416 | 417 | 418 | //printf("find hv 0x%x %dx%d\n", dscans[0].hv, *width,*height); 419 | switch (dscans[0].hv) { 420 | case 0x22: // 411 421 | mb=6; 422 | mcusx = *width >> 4; 423 | mcusy = *height >> 4; 424 | bpp=2; 425 | xpitch = 16 * bpp; 426 | pitch = *width * bpp; // YUYV out 427 | ypitch = 16 * pitch; 428 | convert = yuv420pto422; 429 | break; 430 | case 0x21: //422 431 | // printf("find 422 %dx%d\n",*width,*height); 432 | mb=4; 433 | mcusx = *width >> 4; 434 | mcusy = *height >> 3; 435 | bpp=2; 436 | xpitch = 16 * bpp; 437 | pitch = *width * bpp; // YUYV out 438 | ypitch = 8 * pitch; 439 | convert = yuv422pto422; 440 | break; 441 | case 0x11: //444 442 | mcusx = *width >> 3; 443 | mcusy = *height >> 3; 444 | bpp=2; 445 | xpitch = 8 * bpp; 446 | pitch = *width * bpp; // YUYV out 447 | ypitch = 8 * pitch; 448 | if (info.ns==1) { 449 | mb = 1; 450 | convert = yuv400pto422; 451 | } else { 452 | mb=3; 453 | convert = yuv444pto422; 454 | } 455 | break; 456 | default: 457 | err = ERR_NOT_YCBCR_221111; 458 | goto error; 459 | break; 460 | } 461 | 462 | idctqtab(quant[dscans[0].tq], decdata->dquant[0]); 463 | idctqtab(quant[dscans[1].tq], decdata->dquant[1]); 464 | idctqtab(quant[dscans[2].tq], decdata->dquant[2]); 465 | setinput(&in, datap); 466 | dec_initscans(); 467 | 468 | dscans[0].next = 2; 469 | dscans[1].next = 1; 470 | dscans[2].next = 0; /* 4xx encoding */ 471 | for (my = 0,y=0; my < mcusy; my++,y+=ypitch) { 472 | for (mx = 0,x=0; mx < mcusx; mx++,x+=xpitch) { 473 | if (info.dri && !--info.nm) 474 | if (dec_checkmarker()) { 475 | err = ERR_WRONG_MARKER; 476 | goto error; 477 | } 478 | switch (mb){ 479 | case 6: { 480 | decode_mcus(&in, decdata->dcts, mb, dscans, max); 481 | idct(decdata->dcts, decdata->out, decdata->dquant[0], 482 | IFIX(128.5), max[0]); 483 | idct(decdata->dcts + 64, decdata->out + 64, 484 | decdata->dquant[0], IFIX(128.5), max[1]); 485 | idct(decdata->dcts + 128, decdata->out + 128, 486 | decdata->dquant[0], IFIX(128.5), max[2]); 487 | idct(decdata->dcts + 192, decdata->out + 192, 488 | decdata->dquant[0], IFIX(128.5), max[3]); 489 | idct(decdata->dcts + 256, decdata->out + 256, 490 | decdata->dquant[1], IFIX(0.5), max[4]); 491 | idct(decdata->dcts + 320, decdata->out + 320, 492 | decdata->dquant[2], IFIX(0.5), max[5]); 493 | 494 | } break; 495 | case 4: 496 | { 497 | decode_mcus(&in, decdata->dcts, mb, dscans, max); 498 | idct(decdata->dcts, decdata->out, decdata->dquant[0], 499 | IFIX(128.5), max[0]); 500 | idct(decdata->dcts + 64, decdata->out + 64, 501 | decdata->dquant[0], IFIX(128.5), max[1]); 502 | idct(decdata->dcts + 128, decdata->out + 256, 503 | decdata->dquant[1], IFIX(0.5), max[4]); 504 | idct(decdata->dcts + 192, decdata->out + 320, 505 | decdata->dquant[2], IFIX(0.5), max[5]); 506 | 507 | } 508 | break; 509 | case 3: 510 | decode_mcus(&in, decdata->dcts, mb, dscans, max); 511 | idct(decdata->dcts, decdata->out, decdata->dquant[0], 512 | IFIX(128.5), max[0]); 513 | idct(decdata->dcts + 64, decdata->out + 256, 514 | decdata->dquant[1], IFIX(0.5), max[4]); 515 | idct(decdata->dcts + 128, decdata->out + 320, 516 | decdata->dquant[2], IFIX(0.5), max[5]); 517 | 518 | 519 | break; 520 | case 1: 521 | decode_mcus(&in, decdata->dcts, mb, dscans, max); 522 | idct(decdata->dcts, decdata->out, decdata->dquant[0], 523 | IFIX(128.5), max[0]); 524 | 525 | break; 526 | 527 | } // switch enc411 528 | convert(decdata->out,*pic+y+x,pitch); 529 | } 530 | } 531 | 532 | m = dec_readmarker(&in); 533 | if (m != M_EOI) { 534 | err = ERR_NO_EOI; 535 | goto error; 536 | } 537 | if (decdata) 538 | free(decdata); 539 | return 0; 540 | error: 541 | if (decdata) 542 | free(decdata); 543 | return err; 544 | } 545 | 546 | /****************************************************************/ 547 | /************** huffman decoder ***************/ 548 | /****************************************************************/ 549 | static int huffman_init(void) 550 | { int tc, th, tt; 551 | const unsigned char *ptr= JPEGHuffmanTable ; 552 | int i, j, l; 553 | l = JPG_HUFFMAN_TABLE_LENGTH ; 554 | while (l > 0) { 555 | int hufflen[16], k; 556 | unsigned char huffvals[256]; 557 | 558 | tc = *ptr++; 559 | th = tc & 15; 560 | tc >>= 4; 561 | tt = tc * 2 + th; 562 | if (tc > 1 || th > 1) 563 | return -ERR_BAD_TABLES; 564 | for (i = 0; i < 16; i++) 565 | hufflen[i] = *ptr++; 566 | l -= 1 + 16; 567 | k = 0; 568 | for (i = 0; i < 16; i++) { 569 | for (j = 0; j < hufflen[i]; j++) 570 | huffvals[k++] = *ptr++; 571 | l -= hufflen[i]; 572 | } 573 | dec_makehuff(dhuff + tt, hufflen, huffvals); 574 | } 575 | return 0; 576 | } 577 | 578 | static int fillbits __P((struct in *, int, unsigned int)); 579 | static int dec_rec2 580 | __P((struct in *, struct dec_hufftbl *, int *, int, int)); 581 | 582 | static void setinput(in, p) 583 | struct in *in; 584 | unsigned char *p; 585 | { 586 | in->p = p; 587 | in->left = 0; 588 | in->bits = 0; 589 | in->marker = 0; 590 | } 591 | 592 | static int fillbits(in, le, bi) 593 | struct in *in; 594 | int le; 595 | unsigned int bi; 596 | { 597 | int b, m; 598 | 599 | if (in->marker) { 600 | if (le <= 16) 601 | in->bits = bi << 16, le += 16; 602 | return le; 603 | } 604 | while (le <= 24) { 605 | b = *in->p++; 606 | if (b == 0xff && (m = *in->p++) != 0) { 607 | if (m == M_EOF) { 608 | if (in->func && (m = in->func(in->data)) == 0) 609 | continue; 610 | } 611 | in->marker = m; 612 | if (le <= 16) 613 | bi = bi << 16, le += 16; 614 | break; 615 | } 616 | bi = bi << 8 | b; 617 | le += 8; 618 | } 619 | in->bits = bi; /* tmp... 2 return values needed */ 620 | return le; 621 | } 622 | 623 | static int dec_readmarker(in) 624 | struct in *in; 625 | { 626 | int m; 627 | 628 | in->left = fillbits(in, in->left, in->bits); 629 | if ((m = in->marker) == 0) 630 | return 0; 631 | in->left = 0; 632 | in->marker = 0; 633 | return m; 634 | } 635 | 636 | #define LEBI_DCL int le, bi 637 | #define LEBI_GET(in) (le = in->left, bi = in->bits) 638 | #define LEBI_PUT(in) (in->left = le, in->bits = bi) 639 | 640 | #define GETBITS(in, n) ( \ 641 | (le < (n) ? le = fillbits(in, le, bi), bi = in->bits : 0), \ 642 | (le -= (n)), \ 643 | bi >> le & ((1 << (n)) - 1) \ 644 | ) 645 | 646 | #define UNGETBITS(in, n) ( \ 647 | le += (n) \ 648 | ) 649 | 650 | 651 | static int dec_rec2(in, hu, runp, c, i) 652 | struct in *in; 653 | struct dec_hufftbl *hu; 654 | int *runp; 655 | int c, i; 656 | { 657 | LEBI_DCL; 658 | 659 | LEBI_GET(in); 660 | if (i) { 661 | UNGETBITS(in, i & 127); 662 | *runp = i >> 8 & 15; 663 | i >>= 16; 664 | } else { 665 | for (i = DECBITS; 666 | (c = ((c << 1) | GETBITS(in, 1))) >= (hu->maxcode[i]); i++); 667 | if (i >= 16) { 668 | in->marker = M_BADHUFF; 669 | return 0; 670 | } 671 | i = hu->vals[hu->valptr[i] + c - hu->maxcode[i - 1] * 2]; 672 | *runp = i >> 4; 673 | i &= 15; 674 | } 675 | if (i == 0) { /* sigh, 0xf0 is 11 bit */ 676 | LEBI_PUT(in); 677 | return 0; 678 | } 679 | /* receive part */ 680 | c = GETBITS(in, i); 681 | if (c < (1 << (i - 1))) 682 | c += (-1 << i) + 1; 683 | LEBI_PUT(in); 684 | return c; 685 | } 686 | 687 | #define DEC_REC(in, hu, r, i) ( \ 688 | r = GETBITS(in, DECBITS), \ 689 | i = hu->llvals[r], \ 690 | i & 128 ? \ 691 | ( \ 692 | UNGETBITS(in, i & 127), \ 693 | r = i >> 8 & 15, \ 694 | i >> 16 \ 695 | ) \ 696 | : \ 697 | ( \ 698 | LEBI_PUT(in), \ 699 | i = dec_rec2(in, hu, &r, r, i), \ 700 | LEBI_GET(in), \ 701 | i \ 702 | ) \ 703 | ) 704 | 705 | static void decode_mcus(in, dct, n, sc, maxp) 706 | struct in *in; 707 | int *dct; 708 | int n; 709 | struct scan *sc; 710 | int *maxp; 711 | { 712 | struct dec_hufftbl *hu; 713 | int i, r, t; 714 | LEBI_DCL; 715 | 716 | memset(dct, 0, n * 64 * sizeof(*dct)); 717 | LEBI_GET(in); 718 | while (n-- > 0) { 719 | hu = sc->hudc.dhuff; 720 | *dct++ = (sc->dc += DEC_REC(in, hu, r, t)); 721 | 722 | hu = sc->huac.dhuff; 723 | i = 63; 724 | while (i > 0) { 725 | t = DEC_REC(in, hu, r, t); 726 | if (t == 0 && r == 0) { 727 | dct += i; 728 | break; 729 | } 730 | dct += r; 731 | *dct++ = t; 732 | i -= r + 1; 733 | } 734 | *maxp++ = 64 - i; 735 | if (n == sc->next) 736 | sc++; 737 | } 738 | LEBI_PUT(in); 739 | } 740 | 741 | static void dec_makehuff(hu, hufflen, huffvals) 742 | struct dec_hufftbl *hu; 743 | int *hufflen; 744 | unsigned char *huffvals; 745 | { 746 | int code, k, i, j, d, x, c, v; 747 | for (i = 0; i < (1 << DECBITS); i++) 748 | hu->llvals[i] = 0; 749 | 750 | /* 751 | * llvals layout: 752 | * 753 | * value v already known, run r, backup u bits: 754 | * vvvvvvvvvvvvvvvv 0000 rrrr 1 uuuuuuu 755 | * value unknown, size b bits, run r, backup u bits: 756 | * 000000000000bbbb 0000 rrrr 0 uuuuuuu 757 | * value and size unknown: 758 | * 0000000000000000 0000 0000 0 0000000 759 | */ 760 | code = 0; 761 | k = 0; 762 | for (i = 0; i < 16; i++, code <<= 1) { /* sizes */ 763 | hu->valptr[i] = k; 764 | for (j = 0; j < hufflen[i]; j++) { 765 | hu->vals[k] = *huffvals++; 766 | if (i < DECBITS) { 767 | c = code << (DECBITS - 1 - i); 768 | v = hu->vals[k] & 0x0f; /* size */ 769 | for (d = 1 << (DECBITS - 1 - i); --d >= 0;) { 770 | if (v + i < DECBITS) { /* both fit in table */ 771 | x = d >> (DECBITS - 1 - v - i); 772 | if (v && x < (1 << (v - 1))) 773 | x += (-1 << v) + 1; 774 | x = x << 16 | (hu->vals[k] & 0xf0) << 4 | 775 | (DECBITS - (i + 1 + v)) | 128; 776 | } else 777 | x = v << 16 | (hu->vals[k] & 0xf0) << 4 | 778 | (DECBITS - (i + 1)); 779 | hu->llvals[c | d] = x; 780 | } 781 | } 782 | code++; 783 | k++; 784 | } 785 | hu->maxcode[i] = code; 786 | } 787 | hu->maxcode[16] = 0x20000; /* always terminate decode */ 788 | } 789 | 790 | /****************************************************************/ 791 | /************** idct ***************/ 792 | /****************************************************************/ 793 | 794 | 795 | #define IMULT(a, b) (((a) * (b)) >> ISHIFT) 796 | #define ITOINT(a) ((a) >> ISHIFT) 797 | 798 | #define S22 ((PREC)IFIX(2 * 0.382683432)) 799 | #define C22 ((PREC)IFIX(2 * 0.923879532)) 800 | #define IC4 ((PREC)IFIX(1 / 0.707106781)) 801 | 802 | static unsigned char zig2[64] = { 803 | 0, 2, 3, 9, 10, 20, 21, 35, 804 | 14, 16, 25, 31, 39, 46, 50, 57, 805 | 5, 7, 12, 18, 23, 33, 37, 48, 806 | 27, 29, 41, 44, 52, 55, 59, 62, 807 | 15, 26, 30, 40, 45, 51, 56, 58, 808 | 1, 4, 8, 11, 19, 22, 34, 36, 809 | 28, 42, 43, 53, 54, 60, 61, 63, 810 | 6, 13, 17, 24, 32, 38, 47, 49 811 | }; 812 | 813 | inline static void idct(int *in, int *out, int *quant, long off, int max) 814 | { 815 | long t0, t1, t2, t3, t4, t5, t6, t7; // t ; 816 | long tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; 817 | long tmp[64], *tmpp; 818 | int i, j, te; 819 | unsigned char *zig2p; 820 | 821 | t0 = off; 822 | if (max == 1) { 823 | t0 += in[0] * quant[0]; 824 | for (i = 0; i < 64; i++) 825 | out[i] = ITOINT(t0); 826 | return; 827 | } 828 | zig2p = zig2; 829 | tmpp = tmp; 830 | for (i = 0; i < 8; i++) { 831 | j = *zig2p++; 832 | t0 += in[j] * (long) quant[j]; 833 | j = *zig2p++; 834 | t5 = in[j] * (long) quant[j]; 835 | j = *zig2p++; 836 | t2 = in[j] * (long) quant[j]; 837 | j = *zig2p++; 838 | t7 = in[j] * (long) quant[j]; 839 | j = *zig2p++; 840 | t1 = in[j] * (long) quant[j]; 841 | j = *zig2p++; 842 | t4 = in[j] * (long) quant[j]; 843 | j = *zig2p++; 844 | t3 = in[j] * (long) quant[j]; 845 | j = *zig2p++; 846 | t6 = in[j] * (long) quant[j]; 847 | 848 | 849 | if ((t1 | t2 | t3 | t4 | t5 | t6 | t7) == 0) { 850 | 851 | tmpp[0 * 8] = t0; 852 | tmpp[1 * 8] = t0; 853 | tmpp[2 * 8] = t0; 854 | tmpp[3 * 8] = t0; 855 | tmpp[4 * 8] = t0; 856 | tmpp[5 * 8] = t0; 857 | tmpp[6 * 8] = t0; 858 | tmpp[7 * 8] = t0; 859 | 860 | tmpp++; 861 | t0 = 0; 862 | continue; 863 | } 864 | //IDCT; 865 | tmp0 = t0 + t1; 866 | t1 = t0 - t1; 867 | tmp2 = t2 - t3; 868 | t3 = t2 + t3; 869 | tmp2 = IMULT(tmp2, IC4) - t3; 870 | tmp3 = tmp0 + t3; 871 | t3 = tmp0 - t3; 872 | tmp1 = t1 + tmp2; 873 | tmp2 = t1 - tmp2; 874 | tmp4 = t4 - t7; 875 | t7 = t4 + t7; 876 | tmp5 = t5 + t6; 877 | t6 = t5 - t6; 878 | tmp6 = tmp5 - t7; 879 | t7 = tmp5 + t7; 880 | tmp5 = IMULT(tmp6, IC4); 881 | tmp6 = IMULT((tmp4 + t6), S22); 882 | tmp4 = IMULT(tmp4, (C22 - S22)) + tmp6; 883 | t6 = IMULT(t6, (C22 + S22)) - tmp6; 884 | t6 = t6 - t7; 885 | t5 = tmp5 - t6; 886 | t4 = tmp4 - t5; 887 | 888 | tmpp[0 * 8] = tmp3 + t7; //t0; 889 | tmpp[1 * 8] = tmp1 + t6; //t1; 890 | tmpp[2 * 8] = tmp2 + t5; //t2; 891 | tmpp[3 * 8] = t3 + t4; //t3; 892 | tmpp[4 * 8] = t3 - t4; //t4; 893 | tmpp[5 * 8] = tmp2 - t5; //t5; 894 | tmpp[6 * 8] = tmp1 - t6; //t6; 895 | tmpp[7 * 8] = tmp3 - t7; //t7; 896 | tmpp++; 897 | t0 = 0; 898 | } 899 | for (i = 0, j = 0; i < 8; i++) { 900 | t0 = tmp[j + 0]; 901 | t1 = tmp[j + 1]; 902 | t2 = tmp[j + 2]; 903 | t3 = tmp[j + 3]; 904 | t4 = tmp[j + 4]; 905 | t5 = tmp[j + 5]; 906 | t6 = tmp[j + 6]; 907 | t7 = tmp[j + 7]; 908 | if ((t1 | t2 | t3 | t4 | t5 | t6 | t7) == 0) { 909 | te = ITOINT(t0); 910 | out[j + 0] = te; 911 | out[j + 1] = te; 912 | out[j + 2] = te; 913 | out[j + 3] = te; 914 | out[j + 4] = te; 915 | out[j + 5] = te; 916 | out[j + 6] = te; 917 | out[j + 7] = te; 918 | j += 8; 919 | continue; 920 | } 921 | //IDCT; 922 | tmp0 = t0 + t1; 923 | t1 = t0 - t1; 924 | tmp2 = t2 - t3; 925 | t3 = t2 + t3; 926 | tmp2 = IMULT(tmp2, IC4) - t3; 927 | tmp3 = tmp0 + t3; 928 | t3 = tmp0 - t3; 929 | tmp1 = t1 + tmp2; 930 | tmp2 = t1 - tmp2; 931 | tmp4 = t4 - t7; 932 | t7 = t4 + t7; 933 | tmp5 = t5 + t6; 934 | t6 = t5 - t6; 935 | tmp6 = tmp5 - t7; 936 | t7 = tmp5 + t7; 937 | tmp5 = IMULT(tmp6, IC4); 938 | tmp6 = IMULT((tmp4 + t6), S22); 939 | tmp4 = IMULT(tmp4, (C22 - S22)) + tmp6; 940 | t6 = IMULT(t6, (C22 + S22)) - tmp6; 941 | t6 = t6 - t7; 942 | t5 = tmp5 - t6; 943 | t4 = tmp4 - t5; 944 | 945 | out[j + 0] = ITOINT(tmp3 + t7); 946 | out[j + 1] = ITOINT(tmp1 + t6); 947 | out[j + 2] = ITOINT(tmp2 + t5); 948 | out[j + 3] = ITOINT(t3 + t4); 949 | out[j + 4] = ITOINT(t3 - t4); 950 | out[j + 5] = ITOINT(tmp2 - t5); 951 | out[j + 6] = ITOINT(tmp1 - t6); 952 | out[j + 7] = ITOINT(tmp3 - t7); 953 | j += 8; 954 | } 955 | 956 | } 957 | 958 | static unsigned char zig[64] = { 959 | 0, 1, 5, 6, 14, 15, 27, 28, 960 | 2, 4, 7, 13, 16, 26, 29, 42, 961 | 3, 8, 12, 17, 25, 30, 41, 43, 962 | 9, 11, 18, 24, 31, 40, 44, 53, 963 | 10, 19, 23, 32, 39, 45, 52, 54, 964 | 20, 22, 33, 38, 46, 51, 55, 60, 965 | 21, 34, 37, 47, 50, 56, 59, 61, 966 | 35, 36, 48, 49, 57, 58, 62, 63 967 | }; 968 | 969 | static PREC aaidct[8] = { 970 | IFIX(0.3535533906), IFIX(0.4903926402), 971 | IFIX(0.4619397663), IFIX(0.4157348062), 972 | IFIX(0.3535533906), IFIX(0.2777851165), 973 | IFIX(0.1913417162), IFIX(0.0975451610) 974 | }; 975 | 976 | 977 | static void idctqtab(qin, qout) 978 | unsigned char *qin; 979 | PREC *qout; 980 | { 981 | int i, j; 982 | 983 | for (i = 0; i < 8; i++) 984 | for (j = 0; j < 8; j++) 985 | qout[zig[i * 8 + j]] = qin[zig[i * 8 + j]] * 986 | IMULT(aaidct[i], aaidct[j]); 987 | } 988 | 989 | #define FOUR_TWO_TWO 2 //Y00 Cb Y01 Cr 990 | 991 | 992 | /* translate YUV422Packed to rgb24 */ 993 | 994 | unsigned int 995 | Pyuv422torgb24(unsigned char * input_ptr, unsigned char * output_ptr, unsigned int image_width, unsigned int image_height) 996 | { 997 | unsigned int i, size; 998 | unsigned char Y, Y1, U, V; 999 | unsigned char *buff = input_ptr; 1000 | unsigned char *output_pt = output_ptr; 1001 | size = image_width * image_height /2; 1002 | for (i = size; i > 0; i--) { 1003 | /* bgr instead rgb ?? */ 1004 | Y = buff[0] ; 1005 | U = buff[1] ; 1006 | Y1 = buff[2]; 1007 | V = buff[3]; 1008 | buff += 4; 1009 | *output_pt++ = R_FROMYV(Y,V); 1010 | *output_pt++ = G_FROMYUV(Y,U,V); //b 1011 | *output_pt++ = B_FROMYU(Y,U); //v 1012 | 1013 | *output_pt++ = R_FROMYV(Y1,V); 1014 | *output_pt++ = G_FROMYUV(Y1,U,V); //b 1015 | *output_pt++ = B_FROMYU(Y1,U); //v 1016 | } 1017 | 1018 | return FOUR_TWO_TWO; 1019 | } 1020 | 1021 | static void yuv420pto422(int * out,unsigned char *pic,int width) 1022 | { 1023 | int j, k; 1024 | unsigned char *pic0, *pic1; 1025 | int *outy, *outu, *outv; 1026 | int outy1 = 0; 1027 | int outy2 = 8; 1028 | 1029 | 1030 | pic0 = pic; 1031 | pic1 = pic + width; 1032 | outy = out; 1033 | outu = out + 64 * 4; 1034 | outv = out + 64 * 5; 1035 | for (j = 0; j < 8; j++) { 1036 | for (k = 0; k < 8; k++) { 1037 | if( k == 4) { 1038 | outy1 += 56; 1039 | outy2 += 56; 1040 | } 1041 | *pic0++ = CLIP(outy[outy1]); 1042 | *pic0++ = CLIP(128 + *outu); 1043 | *pic0++ = CLIP(outy[outy1+1]); 1044 | *pic0++ = CLIP(128 + *outv); 1045 | *pic1++ = CLIP(outy[outy2]); 1046 | *pic1++ = CLIP(128 + *outu); 1047 | *pic1++ = CLIP(outy[outy2+1]); 1048 | *pic1++ = CLIP(128 + *outv); 1049 | outy1 +=2; outy2 += 2; outu++; outv++; 1050 | } 1051 | if(j==3) { 1052 | outy = out + 128; 1053 | } else { 1054 | outy += 16; 1055 | } 1056 | outy1 = 0; 1057 | outy2 = 8; 1058 | pic0 += 2 * (width -16); 1059 | pic1 += 2 * (width -16); 1060 | 1061 | } 1062 | 1063 | } 1064 | static void yuv422pto422(int * out,unsigned char *pic,int width) 1065 | { 1066 | int j, k; 1067 | unsigned char *pic0, *pic1; 1068 | int *outy, *outu, *outv; 1069 | int outy1 = 0; 1070 | int outy2 = 8; 1071 | int outu1 = 0; 1072 | int outv1 = 0; 1073 | 1074 | 1075 | pic0 = pic; 1076 | pic1 = pic + width; 1077 | outy = out; 1078 | outu = out + 64 * 4; 1079 | outv = out + 64 * 5; 1080 | for (j = 0; j < 4; j++) { 1081 | for (k = 0; k < 8; k++) { 1082 | if( k == 4) { 1083 | outy1 += 56; 1084 | outy2 += 56; 1085 | } 1086 | *pic0++ = CLIP(outy[outy1]); 1087 | *pic0++ = CLIP(128 + outu[outu1]); 1088 | *pic0++ = CLIP(outy[outy1+1]); 1089 | *pic0++ = CLIP(128 + outv[outv1]); 1090 | *pic1++ = CLIP(outy[outy2]); 1091 | *pic1++ = CLIP(128 + outu[outu1+8]); 1092 | *pic1++ = CLIP(outy[outy2+1]); 1093 | *pic1++ = CLIP(128 + outv[outv1+8]); 1094 | outv1 += 1; outu1 += 1; 1095 | outy1 +=2; outy2 +=2; 1096 | 1097 | } 1098 | 1099 | outy += 16;outu +=8; outv +=8; 1100 | outv1 = 0; outu1=0; 1101 | outy1 = 0; 1102 | outy2 = 8; 1103 | pic0 += 2 * (width -16); 1104 | pic1 += 2 * (width -16); 1105 | 1106 | } 1107 | 1108 | } 1109 | static void yuv444pto422(int * out,unsigned char *pic,int width) 1110 | { 1111 | int j, k; 1112 | unsigned char *pic0, *pic1; 1113 | int *outy, *outu, *outv; 1114 | int outy1 = 0; 1115 | int outy2 = 8; 1116 | int outu1 = 0; 1117 | int outv1 = 0; 1118 | 1119 | pic0 = pic; 1120 | pic1 = pic + width; 1121 | outy = out; 1122 | outu = out + 64 * 4; // Ooops where did i invert ?? 1123 | outv = out + 64 * 5; 1124 | for (j = 0; j < 4; j++) { 1125 | for (k = 0; k < 4; k++) { 1126 | 1127 | *pic0++ =CLIP( outy[outy1]); 1128 | *pic0++ =CLIP( 128 + outu[outu1]); 1129 | *pic0++ =CLIP( outy[outy1+1]); 1130 | *pic0++ =CLIP( 128 + outv[outv1]); 1131 | *pic1++ =CLIP( outy[outy2]); 1132 | *pic1++ =CLIP( 128 + outu[outu1+8]); 1133 | *pic1++ =CLIP( outy[outy2+1]); 1134 | *pic1++ =CLIP( 128 + outv[outv1+8]); 1135 | outv1 += 2; outu1 += 2; 1136 | outy1 +=2; outy2 +=2; 1137 | } 1138 | outy += 16;outu +=16; outv +=16; 1139 | outv1 = 0; outu1=0; 1140 | outy1 = 0; 1141 | outy2 = 8; 1142 | pic0 += 2 * (width -8); 1143 | pic1 += 2 * (width -8); 1144 | } 1145 | 1146 | } 1147 | static void yuv400pto422(int * out,unsigned char *pic,int width) 1148 | { 1149 | int j, k; 1150 | unsigned char *pic0, *pic1; 1151 | int *outy ; 1152 | int outy1 = 0; 1153 | int outy2 = 8; 1154 | pic0 = pic; 1155 | pic1 = pic + width; 1156 | outy = out; 1157 | 1158 | for (j = 0; j < 4; j++) { 1159 | for (k = 0; k < 4; k++) { 1160 | *pic0++ = CLIP(outy[outy1]); 1161 | *pic0++ = 128 ; 1162 | *pic0++ = CLIP(outy[outy1+1]); 1163 | *pic0++ = 128 ; 1164 | *pic1++ = CLIP(outy[outy2]); 1165 | *pic1++ = 128 ; 1166 | *pic1++ = CLIP(outy[outy2+1]); 1167 | *pic1++ = 128 ; 1168 | outy1 +=2; outy2 +=2; 1169 | } 1170 | outy += 16; 1171 | outy1 = 0; 1172 | outy2 = 8; 1173 | pic0 += 2 * (width -8); 1174 | pic1 += 2 * (width -8); 1175 | } 1176 | 1177 | } 1178 | 1179 | int 1180 | is_huffman(unsigned char *buf) 1181 | { 1182 | unsigned char *ptbuf; 1183 | int i = 0; 1184 | ptbuf = buf; 1185 | while (((ptbuf[0] << 8) | ptbuf[1]) != 0xffda){ 1186 | if(i++ > 2048) 1187 | return 0; 1188 | if(((ptbuf[0] << 8) | ptbuf[1]) == 0xffc4) 1189 | return 1; 1190 | ptbuf++; 1191 | } 1192 | return 0; 1193 | } 1194 | static void 1195 | getPictureName (char *Picture, int fmt) 1196 | { 1197 | char temp[80]; 1198 | char *myext[] = { "pnm", "jpg" }; 1199 | // int i; 1200 | time_t curdate; 1201 | struct tm *tdate; 1202 | memset (temp, '\0', sizeof (temp)); 1203 | time (&curdate); 1204 | tdate = localtime (&curdate); 1205 | snprintf (temp, 26, "P-%02d_%02d_%04d-%02d_%02d_%02d.%s", 1206 | tdate->tm_mon + 1, tdate->tm_mday, tdate->tm_year + 1900, 1207 | tdate->tm_hour, tdate->tm_min, tdate->tm_sec, myext[fmt]); 1208 | 1209 | memcpy (Picture, temp, strlen (temp)); 1210 | } 1211 | int 1212 | get_picture(unsigned char *buf,int size) 1213 | { 1214 | FILE *file; 1215 | unsigned char *ptdeb,*ptcur = buf; 1216 | int sizein; 1217 | char *name = NULL; 1218 | name = calloc(80,1); 1219 | getPictureName (name, 1); 1220 | file = fopen(name, "wb"); 1221 | if (file != NULL) { 1222 | if(!is_huffman(buf)){ 1223 | ptdeb = ptcur = buf; 1224 | while (((ptcur[0] << 8) | ptcur[1]) != 0xffc0) 1225 | ptcur++; 1226 | sizein = ptcur-ptdeb; 1227 | fwrite(buf,sizein, 1, file); 1228 | fwrite(dht_data, DHT_SIZE, 1, file); 1229 | fwrite(ptcur,size-sizein,1,file); 1230 | } else { 1231 | fwrite(ptcur,size,1,file); /* ptcur was uninit -wsr */ 1232 | } 1233 | fclose(file); 1234 | } 1235 | if(name) 1236 | free(name); 1237 | return 0; 1238 | } 1239 | 1240 | int 1241 | get_pictureYV2(unsigned char *buf,int width,int height) 1242 | { 1243 | FILE *foutpict; 1244 | unsigned char *picture = NULL; 1245 | char *name = NULL; 1246 | name = calloc(80,1); 1247 | getPictureName (name, 0); 1248 | picture = (unsigned char *)malloc(width*height*3*sizeof(char)); 1249 | if(picture){ 1250 | Pyuv422torgb24(buf, picture, width, height); 1251 | }else{ 1252 | printf(" no room to take a picture \n"); 1253 | return 0; 1254 | } 1255 | if(name){ 1256 | foutpict = fopen (name, "wb"); 1257 | fprintf (foutpict, "P6\n%d %d\n255\n", width, height); 1258 | fwrite (picture, sizeof (char), width * height * 3, foutpict); 1259 | fclose (foutpict); 1260 | free(name); 1261 | } 1262 | free(picture); 1263 | picture = NULL; 1264 | return 0; 1265 | } 1266 | 1267 | void yuyv2yuv420sp(unsigned char* yuv420sp, unsigned char* yuv, int width, int height) 1268 | { 1269 | int i = 0; 1270 | int j = 0; 1271 | int k = 0; 1272 | unsigned char* p_y; 1273 | unsigned char* p_uv; 1274 | unsigned char* p_yuv; 1275 | int yuv_size = 0; 1276 | 1277 | int idxy = 0; 1278 | int idxu = 0; 1279 | int idxv = 0; 1280 | int out_idxu = 0; 1281 | int out_idxv = 1; 1282 | 1283 | p_yuv = yuv; 1284 | 1285 | p_y = yuv420sp; 1286 | p_uv = p_y + width * height; 1287 | yuv_size = width * height * 2; 1288 | 1289 | // 格式 1290 | idxy = 0; // y 1291 | idxu = 1; // u 1292 | // y 1293 | idxv = 3; // v 1294 | 1295 | // Y分量 ,每个Y元素均需要 1296 | for (i = 0, j = 0; i < yuv_size; i += 2, j++) 1297 | { 1298 | p_y[j] = p_yuv[idxy + i]; 1299 | } 1300 | // UV分量 1301 | for (i = 0; i < height; i += 2) // 隔行提取,从而实现从422到420转换 1302 | { 1303 | for (j = 0; j < width * 2; j += 4) // 扫描所有元素(大小为w*h*2),故这里宽为w+2,一次处理4个字节 1304 | { 1305 | p_uv[k + out_idxu] = p_yuv[j + i*width * 2 + idxu]; 1306 | p_uv[k + out_idxv] = p_yuv[j + i*width * 2 + idxv]; 1307 | k += 2; 1308 | } 1309 | } 1310 | } 1311 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | # luvcview: Sdl video Usb Video Class grabber . # 3 | #This package work with the Logitech UVC based webcams with the mjpeg feature. # 4 | #All the decoding is in user space with the embedded jpeg decoder # 5 | #. # 6 | # Copyright (C) 2005 2006Laurent Pinchart && Michel Xhaard # 7 | # # 8 | # This program is free software; you can redistribute it and/or modify # 9 | # it under the terms of the GNU General Public License as published by # 10 | # the Free Software Foundation; either version 2 of the License, or # 11 | # (at your option) any later version. # 12 | # # 13 | # This program is distributed in the hope that it will be useful, # 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # 16 | # GNU General Public License for more details. # 17 | # # 18 | # You should have received a copy of the GNU General Public License # 19 | # along with this program; if not, write to the Free Software # 20 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # 21 | # # 22 | *******************************************************************************/ 23 | 24 | #define ERR_NO_SOI 1 25 | #define ERR_NOT_8BIT 2 26 | #define ERR_HEIGHT_MISMATCH 3 27 | #define ERR_WIDTH_MISMATCH 4 28 | #define ERR_BAD_WIDTH_OR_HEIGHT 5 29 | #define ERR_TOO_MANY_COMPPS 6 30 | #define ERR_ILLEGAL_HV 7 31 | #define ERR_QUANT_TABLE_SELECTOR 8 32 | #define ERR_NOT_YCBCR_221111 9 33 | #define ERR_UNKNOWN_CID_IN_SCAN 10 34 | #define ERR_NOT_SEQUENTIAL_DCT 11 35 | #define ERR_WRONG_MARKER 12 36 | #define ERR_NO_EOI 13 37 | #define ERR_BAD_TABLES 14 38 | #define ERR_DEPTH_MISMATCH 15 39 | 40 | int jpeg_decode(unsigned char **pic, unsigned char *buf, int *width, 41 | int *height); 42 | int 43 | get_picture(unsigned char *buf,int size); 44 | int 45 | get_pictureYV2(unsigned char *buf,int width,int height); 46 | 47 | void yuyv2yuv420sp(unsigned char* yuv420sp, unsigned char* yuv, int width, int height); 48 | -------------------------------------------------------------------------------- /v4l2uvc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * _/_/ _/_/_/ _/_/_/_/_/ 3 | * _/ _/ _/ _/ 4 | * _/ _/ _/ 5 | * _/ _/_/ _/ 6 | * _/ _/ _/ 7 | * _/ _/ _/ _/ _/ 8 | * _/_/ _/_/ _/_/_/ 9 | */ 10 | /** 11 | * Copyleft (C) 2010 Late Lee 12 | * This program is tested on LINUX PLATFORM, WITH GCC 4.x. 13 | * The program is distributed in the hope that it will be 14 | * useful, but WITHOUT ANY WARRANTY. Please feel free to 15 | * use the program, and I feel free to ignore the related 16 | * issues. Any questions or suggestions, or bugs, please 17 | * contact me at 18 | * <$ echo -n "aHR0cDovL3d3dy5sYXRlbGVlLm9yZwo=" | base64 -d> 19 | * or e-mail to 20 | * <$ echo -n "bGF0ZWxlZUAxNjMuY29tCg==" | base64 -d> 21 | * if you want to do this. 22 | * 23 | * @file v4l2uvc.c 24 | * @author Late Lee 25 | * @date Sat Apr 24 2010 26 | * 27 | * @brief 28 | * vedio capture for Vedio Surveillance System(VSS) 29 | * using v4l2 APIs 30 | * 31 | */ 32 | #include "v4l2uvc.h" 33 | #include "utils.h" 34 | 35 | static v4l2_process_cb_ptr v4l2_processcb = v4l2_process; 36 | 37 | /** 38 | * v4l2_open - 打开摄像头 39 | * 40 | * @param vd_info 摄像头信息结构体 41 | * 42 | * @return 成功返回值为0,否则为-1,并提示出错信息 43 | * @note vd_info结构体中的摄像头描述符由此函数指定 44 | */ 45 | int v4l2_open(struct video_info* vd_info) 46 | { 47 | if (!strcmp(vd_info->name, "")) 48 | strcpy(vd_info->name, DEFAULT_VIDEO); 49 | 50 | /* open it --no block */ 51 | vd_info->camfd = open(vd_info->name, O_RDWR /*| O_NONBLOCK | O_CLOEXEC*/, 0); 52 | if (vd_info->camfd < 0) 53 | unix_error_ret("can not open the device"); 54 | debug_msg("open success!\n"); 55 | debug_msg("===============================\n\n"); 56 | 57 | return 0; 58 | } 59 | 60 | extern int release_rk30(struct video_info* vd_info); 61 | 62 | /** 63 | * v4l2_close - 关闭摄像头,并释放内存 64 | * 65 | * @param vd_info 摄像头信息结构体 66 | * 67 | * @return 成功关闭返回值为0,否则为-1,并提示出错信息 68 | */ 69 | int v4l2_close(struct video_info* vd_info) 70 | { 71 | uint16 i = 0; 72 | if (vd_info->is_streaming) /* stop if it is still capturing */ 73 | v4l2_off(vd_info); 74 | 75 | if (vd_info->frame_buffer) 76 | { 77 | free(vd_info->frame_buffer); 78 | vd_info->frame_buffer = NULL; 79 | } 80 | if (vd_info->tmp_buffer) 81 | { 82 | free(vd_info->tmp_buffer); 83 | vd_info->tmp_buffer = NULL; 84 | } 85 | 86 | if (vd_info->mem_mapped) 87 | { 88 | for (i = 0; i < NB_BUFFER; i++) 89 | { 90 | printf("munamp[%d]...\n", i); 91 | if (vd_info->mem[i]) 92 | if (-1 == munmap(vd_info->mem[i], vd_info->buf.length)) 93 | unix_error_ret("munmap"); 94 | } 95 | } 96 | 97 | close(vd_info->camfd); 98 | debug_msg("close OK!\n"); 99 | 100 | return 0; 101 | } 102 | 103 | static int malloc_userdata(struct video_info* vd_info) 104 | { 105 | // 默认w*h*2,即YUV422格式,后面根据实际情况填写 106 | vd_info->frame_size_in = (vd_info->width * vd_info->height << 1); 107 | //printf("will alloc size: %d\n", vd_info->frame_size_in); 108 | switch (vd_info->format) /** 'format' will be also ok */ 109 | { 110 | case V4L2_PIX_FMT_MJPEG: 111 | vd_info->tmp_buffer = 112 | (uint8 *)calloc(1, (size_t)vd_info->frame_size_in); 113 | if (vd_info->tmp_buffer == NULL) 114 | unix_error_ret("unable alloc tmp_buffer"); 115 | vd_info->frame_buffer = 116 | (uint8 *)calloc(1, (size_t)vd_info->width*(vd_info->height+8) * 2); 117 | if (vd_info->frame_buffer == NULL) 118 | unix_error_ret("unable alloc frame_buffer"); 119 | break; 120 | case V4L2_PIX_FMT_YUV422P: 121 | case V4L2_PIX_FMT_YUYV: 122 | case V4L2_PIX_FMT_YYUV: 123 | case V4L2_PIX_FMT_YVYU: 124 | case V4L2_PIX_FMT_UYVY: 125 | case V4L2_PIX_FMT_VYUY: 126 | case V4L2_PIX_FMT_NV16: 127 | case V4L2_PIX_FMT_NV61: 128 | vd_info->frame_buffer = 129 | (uint8 *)calloc(1,(size_t)vd_info->frame_size_in); 130 | if (vd_info->frame_buffer == NULL) 131 | unix_error_ret("unable alloc frame_buffer"); 132 | break; 133 | case V4L2_PIX_FMT_YVU420: 134 | case V4L2_PIX_FMT_NV12: 135 | case V4L2_PIX_FMT_NV21: 136 | vd_info->frame_size_in = (vd_info->width * vd_info->height * 3 / 2); 137 | //printf("will alloc size: %d\n", vd_info->frame_size_in); 138 | vd_info->frame_buffer = 139 | (uint8 *)calloc(1,(size_t)vd_info->frame_size_in); 140 | if (vd_info->frame_buffer == NULL) 141 | unix_error_ret("unable alloc frame_buffer"); 142 | break; 143 | default: 144 | msg_out("v4l2 init error! not support format: 0x%x\n", vd_info->format); 145 | return -1; 146 | break; 147 | } 148 | 149 | return 0; 150 | } 151 | static int setformat(struct video_info* vd_info) 152 | { 153 | if (-1 == ioctl(vd_info->camfd, VIDIOC_QUERYCAP, &vd_info->cap)) 154 | unix_error_ret("query camera failed"); 155 | if (0 == (vd_info->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) 156 | { 157 | debug_msg("video capture not supported.\n"); 158 | return -1; 159 | } 160 | 161 | /* set format */ 162 | /* it would be safe to use 'v4l2_format' */ 163 | memset(&vd_info->fmt, 0, sizeof(struct v4l2_format)); 164 | vd_info->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 165 | vd_info->fmt.fmt.pix.width = vd_info->width; 166 | vd_info->fmt.fmt.pix.height = vd_info->height; 167 | vd_info->fmt.fmt.pix.field =V4L2_FIELD_ANY; 168 | vd_info->fmt.fmt.pix.pixelformat = vd_info->format; 169 | if (-1 == ioctl(vd_info->camfd, VIDIOC_S_FMT, &vd_info->fmt)) 170 | unix_error_ret("unable to set format "); 171 | 172 | return 0; 173 | } 174 | 175 | static int request_buffer_normal(struct video_info* vd_info) 176 | { 177 | int i = 0; 178 | /* request buffers */ 179 | memset(&vd_info->rb, 0, sizeof(struct v4l2_requestbuffers)); 180 | vd_info->rb.count = NB_BUFFER; /* 4 buffers */ 181 | vd_info->rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 182 | vd_info->rb.memory = V4L2_MEMORY_MMAP; 183 | if (-1 == ioctl(vd_info->camfd, VIDIOC_REQBUFS, &vd_info->rb)) 184 | unix_error_ret("unable to allocte buffers"); 185 | 186 | /* map the buffers(4 buffer) */ 187 | for (i = 0; i < NB_BUFFER; i++) 188 | { 189 | memset(&vd_info->buf, 0, sizeof(struct v4l2_buffer)); 190 | vd_info->buf.index = i; 191 | vd_info->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 192 | vd_info->buf.memory = V4L2_MEMORY_MMAP; 193 | if (-1 == ioctl(vd_info->camfd, VIDIOC_QUERYBUF, &vd_info->buf)) 194 | unix_error_ret("unable to query buffer"); 195 | debug_msg("length: %u offset: %p\n", 196 | vd_info->buf.length, (void*)vd_info->buf.m.offset); 197 | /* map it, 0 means anywhere */ 198 | vd_info->mem[i] = 199 | mmap(0, vd_info->buf.length, PROT_READ, MAP_SHARED, 200 | vd_info->camfd, vd_info->buf.m.offset); 201 | /* MAP_FAILED = (void *)-1 */ 202 | if (MAP_FAILED == vd_info->mem[i]) 203 | unix_error_ret("unable to map buffer"); 204 | debug_msg("buffer mapped in addr:%p.\n", vd_info->mem[i]); 205 | } 206 | 207 | /* queue the buffers */ 208 | for (i = 0; i < NB_BUFFER; i++) 209 | { 210 | memset(&vd_info->buf, 0, sizeof(struct v4l2_buffer)); 211 | vd_info->buf.index = i; 212 | vd_info->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 213 | vd_info->buf.memory = V4L2_MEMORY_MMAP; 214 | if (-1 == ioctl(vd_info->camfd, VIDIOC_QBUF, &vd_info->buf)) 215 | unix_error_ret("unable to queue the buffers"); 216 | } 217 | 218 | vd_info->mem_mapped = 1; 219 | return 0; 220 | } 221 | 222 | /** 223 | * v4l2_init - 以指定参数初始化摄像头,该函数包括了打开摄像头 224 | * 225 | * @param vd_info 摄像头信息结构体 226 | * @param format 摄像头支持的格式,如V4L2_PIX_FMT_YUYV 227 | * @param width 图像宽,如640 228 | * @param height 图像高,如480 229 | * 230 | * @return 成功初始化则返回0,否则返回-1,并提示出错信息 231 | */ 232 | int v4l2_init(struct video_info* vd_info) 233 | { 234 | int ret = 0; 235 | 236 | vd_info->is_quit = 0; 237 | 238 | ret = malloc_userdata(vd_info); 239 | if (ret < 0) 240 | unix_error_ret("malloc_userdata failed"); 241 | 242 | ret = v4l2_open(vd_info); 243 | if (ret < 0) 244 | unix_error_ret("v4l2_open failed"); 245 | 246 | ret = setformat(vd_info); 247 | if (ret < 0) 248 | unix_error_ret("setformat failed"); 249 | 250 | ret = request_buffer_normal(vd_info); 251 | if (ret < 0) 252 | unix_error_ret("request_buffer_normal failed"); 253 | 254 | debug_msg("v4l2 init OK!\n"); 255 | debug_msg("===============================\n\n"); 256 | 257 | return 0; 258 | } 259 | 260 | /** 261 | * v4l2_on - 启动摄像头采集数据 262 | * 263 | * @param vd_info 摄像头信息结构体 264 | * 265 | * @return 成功开始采集则返回0,否则返回-1,并提示出错信息 266 | */ 267 | int v4l2_on(struct video_info* vd_info) 268 | { 269 | vd_info->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 270 | 271 | if (-1 == ioctl(vd_info->camfd, VIDIOC_STREAMON, &vd_info->type)) 272 | unix_error_ret("unable to start capture"); 273 | vd_info->is_streaming = 1; 274 | debug_msg("stream on OK!!\n"); 275 | debug_msg("===============================\n\n"); 276 | 277 | return 0; 278 | } 279 | 280 | /** 281 | * v4l2_off - 停止采集摄像头数据 282 | * 283 | * @param vd_info 摄像头信息结构体 284 | * 285 | * @return 成功则返回0,否则返回-1,并提示出错信息 286 | */ 287 | int v4l2_off(struct video_info* vd_info) 288 | { 289 | vd_info->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 290 | if (-1 == ioctl(vd_info->camfd, VIDIOC_STREAMOFF, &vd_info->type)) 291 | unix_error_ret("unable to stop capture"); 292 | vd_info->is_streaming = 0; 293 | debug_msg("stream off OK!\n"); 294 | debug_msg("===============================\n\n"); 295 | 296 | return 0; 297 | } 298 | 299 | /** 300 | * v4l2_get_pic - 获取图片 301 | * 302 | * @param vd_info 摄像头信息结构体 303 | * 304 | * @return 成功则返回0,否则返回-1,并提示出错信息 305 | */ 306 | int v4l2_get_pic(struct video_info* vd_info) 307 | { 308 | switch(vd_info->format){ 309 | case V4L2_PIX_FMT_MJPEG: 310 | get_picture(vd_info->tmp_buffer,vd_info->buf.bytesused); 311 | break; 312 | case V4L2_PIX_FMT_YUYV: 313 | get_pictureYV2(vd_info->frame_buffer,vd_info->width,vd_info->height); 314 | break; 315 | default: 316 | break; 317 | } 318 | return 0; 319 | } 320 | 321 | 322 | /** 323 | * v4l2_process - 摄像头数据处理 324 | * 325 | * @param vd_info 摄像头信息结构体 326 | * 327 | * @return 成功则返回0,否则返回-1,并提示出错信息 328 | * @note 如果摄像头为MJPEG格式,则可以直接将tmp_buff的数据写到文件, 329 | 保存为jpg格式,即为一张图片。这里只是默认空处理 330 | */ 331 | int v4l2_process(struct video_info* vd_info) 332 | { 333 | //v4l2_get_pic(vd_info); 334 | 335 | /* OK here! */ 336 | #if 0 337 | FILE* fp; 338 | char* file_name = "raw.jpg"; 339 | fp = fopen(file_name, "w"); 340 | if (NULL == fp) 341 | unix_error_ret("unable to open the file"); 342 | fwrite(vd_info->tmp_buffer, vd_info->width, vd_info->height*2, fp); 343 | debug_msg("writing...\n"); 344 | fclose(fp); 345 | #endif 346 | 347 | //debug_msg("default process func...\n"); 348 | 349 | return 0; 350 | } 351 | 352 | int v4l2_set_processcb(v4l2_process_cb_ptr ptr) 353 | { 354 | v4l2_processcb = ptr; 355 | 356 | return 0; 357 | } 358 | 359 | 360 | /** 361 | * v4l2_readframe - 采集摄像头数据 362 | * 363 | * @param vd_info 摄像头信息结构体 364 | * 365 | * @return 成功则返回0,否则返回-1,并提示出错信息 366 | */ 367 | int v4l2_readframe(struct video_info* vd_info) 368 | { 369 | #define HEADFRAME1 0xaf 370 | static int count = 0; 371 | int rett; 372 | 373 | if (!vd_info->is_streaming) /* if stream is off, start it */ 374 | { 375 | if (v4l2_on(vd_info)) /* failed */ 376 | goto err; 377 | } 378 | 379 | /* msg_out("start...\n"); */ 380 | memset(&vd_info->buf, 0, sizeof(struct v4l2_buffer)); 381 | vd_info->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 382 | vd_info->buf.memory = V4L2_MEMORY_MMAP;//V4L2_MEMORY_OVERLAY; //V4L2_MEMORY_MMAP; 383 | rett = ioctl(vd_info->camfd, VIDIOC_DQBUF, &vd_info->buf); 384 | //printf("DQBUF ret: %d: %d:%s\n", rett, errno, strerror(errno)); 385 | /* get data from buffers */ 386 | if (-1 == rett) 387 | { 388 | perror("unable to dequeue buffer: "); 389 | goto err; 390 | } 391 | 392 | vd_info->frame_format = vd_info->format; // 数据帧格式一般与设备摄像头的格式一致,但也有例外 393 | switch (vd_info->format) 394 | { 395 | case V4L2_PIX_FMT_MJPEG: 396 | if (vd_info->buf.bytesused <= HEADFRAME1) 397 | { 398 | msg_out("ignore empty frame...\n"); 399 | return 0; 400 | } 401 | /* we can save tmp_buff to a jpg file,just write it! */ 402 | memcpy(vd_info->tmp_buffer, vd_info->mem[vd_info->buf.index], 403 | vd_info->buf.bytesused); 404 | //printf("decoding frame %d...\n", count); 405 | /* here decode MJPEG,so we can dispaly it */ 406 | // tocheck:在一款摄像头,解码输出的是yuyv格式(非平面) 407 | if (jpeg_decode(&vd_info->frame_buffer, vd_info->tmp_buffer, 408 | &vd_info->width, &vd_info->height) < 0 ) 409 | { 410 | msg_out("decode jpeg error\n"); 411 | goto err; 412 | } 413 | vd_info->frame_format = V4L2_PIX_FMT_YUYV; // jpeg_decode解码得到的是YUYV格式 414 | break; 415 | case V4L2_PIX_FMT_YUYV: 416 | case V4L2_PIX_FMT_YYUV: 417 | case V4L2_PIX_FMT_YVYU: 418 | case V4L2_PIX_FMT_UYVY: 419 | case V4L2_PIX_FMT_VYUY: 420 | case V4L2_PIX_FMT_NV12: 421 | case V4L2_PIX_FMT_NV21: 422 | case V4L2_PIX_FMT_NV16: 423 | case V4L2_PIX_FMT_NV61: 424 | if (vd_info->buf.bytesused > vd_info->frame_size_in) 425 | memcpy(vd_info->frame_buffer, vd_info->mem[vd_info->buf.index], 426 | (size_t)vd_info->frame_size_in); 427 | else 428 | memcpy(vd_info->frame_buffer, vd_info->mem[vd_info->buf.index], 429 | (size_t)vd_info->buf.bytesused); 430 | break; 431 | default: 432 | goto err; 433 | break; 434 | } 435 | 436 | /* here you can process the frame! */ 437 | v4l2_processcb(vd_info); 438 | /* queue buffer again */ 439 | if (-1 == ioctl(vd_info->camfd, VIDIOC_QBUF, &vd_info->buf)) 440 | { 441 | fprintf(stderr,"requeue error\n"); 442 | goto err; 443 | } 444 | 445 | //debug_msg("frame:%d\n", count++); 446 | // vd_info->buf.bytesused 447 | //debug_msg("frame size in: %d %d (%dKB)\n", vd_info->buf.bytesused, vd_info->frame_size_in, vd_info->frame_size_in>>10); 448 | 449 | count++; 450 | return 0; 451 | err: 452 | vd_info->is_quit = 1; 453 | return -1; 454 | } 455 | 456 | /** 457 | * v4l2_capture - 采集摄像头数据 458 | * 459 | * @param vd_info 摄像头信息结构体 460 | * @note 该函数为测试函数,实际中未使用 461 | */ 462 | void v4l2_capture(struct video_info* vd_info) 463 | { 464 | uint16 count = 10; 465 | // int ret; 466 | debug_msg("start capture\n"); 467 | while (count-- > 0) 468 | { 469 | while (1) 470 | { 471 | #if 0 472 | fd_set fds; 473 | struct timeval tv; 474 | FD_ZERO(&fds); 475 | FD_SET(vd_info->camfd, &fds); 476 | /* timeout */ 477 | tv.tv_sec = 2; 478 | tv.tv_usec = 0; 479 | ret = select(vd_info->camfd, &fds, NULL, NULL, &tv); 480 | if (-1 == ret) /* error */ 481 | { 482 | if (EINTR == errno) 483 | continue; 484 | fprintf(stderr,"select error\n"); 485 | exit(EXIT_FAILURE); 486 | } 487 | if (0 == ret) /* timeout */ 488 | { 489 | fprintf(stderr,"select timeout\n"); 490 | exit(EXIT_FAILURE); 491 | 492 | } 493 | #endif 494 | /* good */ 495 | if (v4l2_readframe(vd_info)) 496 | break; 497 | /* try again if errno = EAGAIN */ 498 | } 499 | } 500 | } 501 | 502 | /** 503 | * v4l2_get_capability - 获取摄像头capability 504 | * 505 | * @param vd_info 摄像头信息结构体 506 | * 507 | * @return 成功则返回0,否则返回-1,并提示出错信息 508 | */ 509 | int v4l2_get_capability(struct video_info* vd_info) 510 | { 511 | memset(&vd_info->cap, 0, sizeof(struct v4l2_capability)); 512 | if ( -1 == ioctl(vd_info->camfd, VIDIOC_QUERYCAP,&(vd_info->cap))) 513 | unix_error_ret("VIDIOC_QUERYCAP"); 514 | 515 | if (!strncmp((char*)vd_info->cap.driver, "uvc", 3)) 516 | { 517 | vd_info->driver_type = V4L2_DRIVER_UVC; // UVC驱动 518 | } 519 | debug_msg("driver:%s\n",vd_info->cap.driver); 520 | debug_msg("card:%s\n",vd_info->cap.card); 521 | debug_msg("bus_info:%s\n",vd_info->cap.bus_info); 522 | debug_msg("version:%d\n",vd_info->cap.version); 523 | 524 | /* 525 | * 0x1:CAPTURE 526 | * 0x04000000:STREAMING 527 | */ 528 | 529 | debug_msg("capability:%x\n",vd_info->cap.capabilities); 530 | 531 | v4l2_std_id std; 532 | int ret = 0; 533 | do { 534 | ret = ioctl(vd_info->camfd, VIDIOC_QUERYSTD, &std); 535 | } while (ret == -1 && errno == EAGAIN); 536 | printf("get std: 0x%llx NTSC: 0x%llx PAL: 0x%llx\n", std, V4L2_STD_NTSC, V4L2_STD_PAL); 537 | switch (std){ 538 | case V4L2_STD_NTSC: 539 | printf("NTSC.\n"); 540 | break; 541 | case V4L2_STD_PAL: 542 | printf("PAL.\n"); 543 | break; 544 | } 545 | 546 | debug_msg("query capability success\n"); 547 | debug_msg("===============================\n\n"); 548 | 549 | return 0; 550 | } 551 | 552 | static int enum_frame_sizes(int dev, int pixfmt, int idx) 553 | { 554 | int ret; 555 | struct v4l2_frmsizeenum fsize; 556 | memset(&fsize, 0, sizeof(fsize)); 557 | fsize.index = idx; 558 | fsize.pixel_format = pixfmt; 559 | while ((ret = ioctl(dev, VIDIOC_ENUM_FRAMESIZES, &fsize)) == 0) 560 | { 561 | //printf("%s: type: %d\n", __func__, fsize.type); 562 | if (fsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) 563 | { 564 | printf("{ discrete: width = %u, height = %u}\n", 565 | fsize.discrete.width, fsize.discrete.height); 566 | 567 | // get frame rate 568 | // ret = enum_frame_intervals(dev,pixfmt,fsize.discrete.width, 569 | // fsize.discrete.height); 570 | 571 | if (ret != 0) 572 | { 573 | printf(" Unable to enumerate frame sizes.\n"); 574 | } 575 | } 576 | else if (fsize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) 577 | { 578 | printf("{ continuous: min { width = %u, height = %u } .. " 579 | "max { width = %u, height = %u } }\n", 580 | fsize.stepwise.min_width, 581 | fsize.stepwise.min_height, 582 | fsize.stepwise.max_width, 583 | fsize.stepwise.max_height); 584 | printf(" Refusing to enumerate frame intervals.\n"); 585 | break; 586 | } 587 | else if (fsize.type == V4L2_FRMSIZE_TYPE_STEPWISE) 588 | { 589 | printf("{ stepwise: min { width = %u, height = %u } .. " 590 | "max { width = %u, height = %u } / " 591 | "stepsize { width = %u, height = %u } }\n", 592 | fsize.stepwise.min_width, 593 | fsize.stepwise.min_height, 594 | fsize.stepwise.max_width, 595 | fsize.stepwise.max_height, 596 | fsize.stepwise.step_width, 597 | fsize.stepwise.step_height); 598 | printf(" Refusing to enumerate frame intervals.\n"); 599 | break; 600 | } 601 | else 602 | { 603 | printf("no type"); 604 | } 605 | fsize.index++; 606 | } 607 | 608 | return 0; 609 | // 如果出错,则不打印信息 610 | if (ret != 0 || errno != EINVAL) 611 | { 612 | printf("ERROR enumerating frame sizes: %d\n", errno); 613 | return errno; 614 | } 615 | return 0; 616 | } 617 | 618 | int v4l2_enum_format(struct video_info* vd_info) 619 | { 620 | struct v4l2_fmtdesc fmt_desc; 621 | int ret = -1; 622 | 623 | memset(&fmt_desc, 0, sizeof(struct v4l2_fmtdesc)); 624 | fmt_desc.index=0; 625 | fmt_desc.type= V4L2_BUF_TYPE_VIDEO_CAPTURE; //V4L2_BUF_TYPE_VIDEO_CAPTURE; 626 | 627 | printf("Support format:\n"); 628 | while((ret = ioctl(vd_info->camfd, VIDIOC_ENUM_FMT, &fmt_desc)) != -1) 629 | { 630 | 631 | printf("%d.%s\n",fmt_desc.index,fmt_desc.description); 632 | printf("{ pixelformat = ''%c%c%c%c'', description = ''%s'' }\n", \ 633 | fmt_desc.pixelformat & 0xFF, \ 634 | (fmt_desc.pixelformat >> 8) & 0xFF, \ 635 | (fmt_desc.pixelformat >> 16) & 0xFF, \ 636 | (fmt_desc.pixelformat >> 24) & 0xFF,\ 637 | fmt_desc.description); 638 | ret = enum_frame_sizes(vd_info->camfd, fmt_desc.pixelformat, fmt_desc.index); 639 | if (ret != 0) 640 | printf("Unable to enumerate frame sizes\n"); 641 | 642 | fmt_desc.index++; 643 | } 644 | 645 | debug_msg("===============================\n\n"); 646 | return 0; 647 | } 648 | 649 | /** 650 | * v4l2_get_format - 获取摄像头格式 651 | * 652 | * @param vd_info 摄像头信息结构体 653 | * 654 | * @return 成功返回0,否则返回-1,并提示出错信息 655 | * @note 656 | * 1、在RF6.02中获得到格式为YUYV,但在FC10中却是MJPEG,未知原因 657 | * 2、v4l2_pix_format结构体成员pixelformat,在某些平台上却是pixelfmt,具体参考vedeodev2.h 658 | * 头文件,原因未找到 659 | * 3、获取可读性的格式的代码片段是参考网上资料的 660 | */ 661 | int v4l2_get_format(struct video_info* vd_info) 662 | { 663 | /* see what format it has */ 664 | /* 665 | * I have two camera, 666 | * one is MJPG, 667 | * the other is YUYV 668 | */ 669 | /* NOTE: 670 | * YUYV in RedFlag6.02 671 | * MJPEG in Fedora10 672 | * what is the matter? 673 | * who tell me? 674 | * 675 | */ 676 | memset(&vd_info->fmt,0,sizeof(struct v4l2_format)); 677 | vd_info->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 678 | if (-1 == ioctl(vd_info->camfd, VIDIOC_G_FMT, &(vd_info->fmt))) 679 | unix_error_ret("can not get format"); 680 | /* 681 | * v4l2_pix_format has "pixelformat" or "pixelfmt" 682 | * see vedeodev2.h get a correct one!! 683 | * redflag with 2.6.28:pixelformat 684 | * fc10 with 2.6.33: pixelfmt 685 | * ??? glic??? 686 | */ 687 | vd_info->format = vd_info->fmt.fmt.pix.pixelformat; 688 | debug_msg("format(num):0x%x\n",vd_info->format); 689 | 690 | /* 691 | * pixel fmt is 32 bit 692 | * it is a four character code:abcd.dcba 693 | * so it is easy to get the code for a integer 694 | * see videodev2.h for more information 695 | */ 696 | char code[5]; 697 | int i=0; 698 | for (i=0; i<4; i++) 699 | code[i] = (vd_info->format & (0xff<> i*8; 700 | code[4] = 0; 701 | debug_msg("fomat(human readable):%s\n",code); 702 | /* get more information here */ 703 | vd_info->width = vd_info->fmt.fmt.pix.width; 704 | vd_info->height = vd_info->fmt.fmt.pix.height; 705 | 706 | // field参考v4l2_field 707 | vd_info->field = vd_info->fmt.fmt.pix.field; 708 | vd_info->bytes_per_line= vd_info->fmt.fmt.pix.bytesperline; 709 | vd_info->size_image = vd_info->fmt.fmt.pix.sizeimage; 710 | 711 | // colorspace参考枚举类型v4l2_colorspace 7表示JPEG 8表示RGB 712 | vd_info->color_space = vd_info->fmt.fmt.pix.colorspace; 713 | vd_info->priv = vd_info->fmt.fmt.pix.priv; 714 | debug_msg("width:%d\n",vd_info->width); 715 | debug_msg("height:%d\n",vd_info->height); 716 | debug_msg("field:%d (1 if no fields)\n",vd_info->field); 717 | debug_msg("bytesperline:%d\n",vd_info->bytes_per_line); 718 | debug_msg("sizeimage:%d\n",vd_info->size_image); 719 | debug_msg("colorspace:%d(8 if RGB colorspace)\n",vd_info->color_space); 720 | debug_msg("private field:%d\n",vd_info->priv); 721 | 722 | 723 | debug_msg("get fomat OK!\n"); 724 | debug_msg("===============================\n\n"); 725 | 726 | return 0; 727 | } 728 | 729 | /** 730 | * v4l2_set_format - 设置摄像头格式 731 | * 732 | * @param vd_info 摄像头信息结构体 733 | * @param format 格式,如V4L2_PIX_FMT_YUYV 734 | * @param width 图像宽度 735 | * @param height 图像高度 736 | * 737 | * @return 成功返回0,否则返回-1,并提示出错信息 738 | */ 739 | int v4l2_set_foramt(struct video_info* vd_info,uint32 format, 740 | uint32 width, uint32 height) 741 | { 742 | debug_msg("set format test\n"); 743 | memset(&vd_info->fmt,0,sizeof(struct v4l2_format)); 744 | vd_info->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 745 | vd_info->fmt.fmt.pix.width = width; 746 | vd_info->fmt.fmt.pix.height = height; 747 | vd_info->fmt.fmt.pix.pixelformat= format; 748 | vd_info->fmt.fmt.pix.field = V4L2_FIELD_ANY; 749 | 750 | if (-1 == ioctl(vd_info->camfd, VIDIOC_S_FMT, &(vd_info->fmt))) 751 | unix_error_ret("unable to set format"); 752 | debug_msg("set format success\n"); 753 | 754 | return 0; 755 | } 756 | -------------------------------------------------------------------------------- /v4l2uvc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * _/_/ _/_/_/ _/_/_/_/_/ 3 | * _/ _/ _/ _/ 4 | * _/ _/ _/ 5 | * _/ _/_/ _/ 6 | * _/ _/ _/ 7 | * _/ _/ _/ _/ _/ 8 | * _/_/ _/_/ _/_/_/ 9 | */ 10 | /** 11 | * Copyleft (C) 2010 Late Lee 12 | * This program is tested on LINUX PLATFORM, WITH GCC 4.x. 13 | * The program is distributed in the hope that it will be 14 | * useful, but WITHOUT ANY WARRANTY. Please feel free to 15 | * use the program, and I feel free to ignore the related 16 | * issues. Any questions or suggestions, or bugs, please 17 | * contact me at 18 | * <$ echo -n "aHR0cDovL3d3dy5sYXRlbGVlLm9yZwo=" | base64 -d> 19 | * or e-mail to 20 | * <$ echo -n "bGF0ZWxlZUAxNjMuY29tCg==" | base64 -d> 21 | * if you want to do this. 22 | * 23 | * @file v4l2uvc.h 24 | * @author Late Lee 25 | * @date Tue Jan 18 2011 26 | * 27 | * @brief 28 | * vedio capture for Vedio Surveillance System(VSS) 29 | * using v4l2 APIs 30 | * 31 | * 32 | */ 33 | 34 | #ifndef _V4L2UVC_H 35 | #define _V4L2UVC_H 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include /* ioctl */ 43 | #include /* for MAP_SHARED etc */ 44 | #include /* select,etc. */ 45 | #include /* open, etc */ 46 | #include 47 | #include 48 | #include "my_types.h" 49 | 50 | /* for debug */ 51 | #define DEBUG 52 | #include "debug-msg.h" 53 | 54 | // 缓冲区个数 TODO:改为参数 55 | #define NB_BUFFER 4 56 | 57 | // 默认的 58 | #define DEFAULT_VIDEO "/dev/video0" 59 | 60 | // 驱动类型 61 | enum v4l2_driver_type { 62 | V4L2_DRIVER_UNKNOWN = 0, 63 | V4L2_DRIVER_UVC = 1, 64 | V4L2_DRIVER_RK30 = 2, 65 | 66 | }; 67 | 68 | /** 自定义的摄像头信息结构体 */ 69 | struct video_info 70 | { 71 | char name[16]; /**< 摄像头文件,由调用者指定,否则使用默认值 */ 72 | int camfd; /**< 摄像头文件描述符,由open系统调用指定 */ 73 | enum v4l2_driver_type driver_type; /**< 驱动类型 1:uvc 2:其它 */ 74 | uint32 format; /**< 摄像头支持的格式,如MJPEG、YUYV等 */ 75 | int width; /**< 图像宽 */ 76 | int height; /**< 图像高 */ 77 | int is_streaming; /**< 开始采集 */ 78 | int is_quit; /**< 退出 */ 79 | 80 | void* mem[NB_BUFFER]; /**< main buffers */ 81 | int mem_mapped; /**< 标志所有的mem是否均由mmap申请 */ 82 | uint8* tmp_buffer; /**< 临时缓冲区,针对MJPEG格式而设 */ 83 | uint8* frame_buffer; /**< 一帧图像缓冲区 */ 84 | uint32 frame_size_in; /**< 一帧图像大小(如果是yuv422格式,大小为宽*高*2) */ 85 | uint32 frame_format; /**< 图像缓冲区数据的格式,如YUYV、NV12. */ 86 | 87 | uint32 bytes_per_line; 88 | uint32 size_image; 89 | 90 | struct v4l2_capability cap; /**< 摄像头capability(属性) */ 91 | struct v4l2_format fmt; /**< 摄像头格式,使用该结构体对摄像头进行设置 */ 92 | struct v4l2_requestbuffers rb; /**< 请求缓冲,一般不超过5个 */ 93 | struct v4l2_buffer buf; /**< buffer */ 94 | enum v4l2_buf_type type; /**< 控制命令字? */ 95 | 96 | enum v4l2_field field; 97 | 98 | enum v4l2_colorspace color_space; 99 | uint32 priv; 100 | }; 101 | 102 | typedef int (*v4l2_process_cb_ptr)(struct video_info* vd_info); 103 | 104 | // 设置用户级别的处理回调函数 105 | int v4l2_set_processcb(v4l2_process_cb_ptr ptr); 106 | 107 | int v4l2_open(struct video_info* vd_info); 108 | int v4l2_close(struct video_info* vd_info); 109 | 110 | int v4l2_init(struct video_info* vd_info); 111 | int v4l2_on(struct video_info* vd_info); 112 | int v4l2_off(struct video_info* vd_info); 113 | 114 | int v4l2_readframe(struct video_info* vd_info); 115 | 116 | void v4l2_capture(struct video_info* vd_info); 117 | 118 | int v4l2_process(struct video_info* vd_info); 119 | 120 | int v4l2_get_pic(struct video_info* vd_info); 121 | 122 | int v4l2_get_capability(struct video_info* vd_info); 123 | int v4l2_enum_format(struct video_info* vd_info); 124 | int v4l2_get_format(struct video_info* vd_info); 125 | int v4l2_set_foramt(struct video_info* vd_info, 126 | uint32 width, uint32 height,uint32 format); 127 | 128 | 129 | #endif /* _V4L2UVC_H */ 130 | -------------------------------------------------------------------------------- /yuv2rgb.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/latelee/CameraDemo_Linux/5e1f696ea42150ddc16fa90a4e97a6d81f8e79bc/yuv2rgb.c -------------------------------------------------------------------------------- /yuv2rgb.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/latelee/CameraDemo_Linux/5e1f696ea42150ddc16fa90a4e97a6d81f8e79bc/yuv2rgb.h --------------------------------------------------------------------------------