GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
Executable
+381
@@ -0,0 +1,381 @@
|
||||
#!/bin/sh
|
||||
|
||||
export LD_LIBRARY_PATH=$PWD:$LD_LIBRARY_PATH
|
||||
|
||||
#insmod libcomposite.ko
|
||||
#insmod usb_f_uvc.ko
|
||||
#insmod u_audio.ko
|
||||
#insmod usb_f_uac1.ko
|
||||
#insmod usb_f_uac2.ko
|
||||
#insmod usb_f_fs.ko
|
||||
#insmod usbmon.ko
|
||||
|
||||
UVC_ENABLE="true"
|
||||
UVC1_ENABLE="false"
|
||||
UAC_ENABLE="true"
|
||||
|
||||
UVC_ENABLE_YUYV="true"
|
||||
UVC_ENABLE_NV21="true"
|
||||
UVC_ENABLE_MJPEG="true"
|
||||
UVC_ENABLE_H264="true"
|
||||
UVC_ENABLE_H265="false"
|
||||
|
||||
# UVC data endpoint transmission mode: iso or bulk
|
||||
UVC_TRANSFER_MODE="isoc"
|
||||
|
||||
# USB speed.
|
||||
# ss: super-speed; hs: high-speed; fs: full-speed.
|
||||
USB_SPEED="hs"
|
||||
|
||||
export VID="0x3337"
|
||||
export PID="0x4321"
|
||||
export MANUFACTURER="XMedia"
|
||||
export PRODUCT="HD USB Camera"
|
||||
export SERIALNUMBER="20251215"
|
||||
|
||||
export YUYV="640x360@30 1280x720@13"
|
||||
export NV21="640x360@30 1280x720@13"
|
||||
export MJPEG="640x360@30 1280x720@30 1920x1080@30"
|
||||
export H264="640x360@30 1280x720@30 1920x1080@30"
|
||||
export H265="640x360@30 1280x720@30 1920x1080@30"
|
||||
|
||||
export YUYV_GUID="YUY2\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71"
|
||||
export YUYV_BITS="16"
|
||||
export NV21_GUID="NV21\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71"
|
||||
export NV21_BITS="12"
|
||||
export H264_GUID="H264\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71"
|
||||
export H265_GUID="H265\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71"
|
||||
|
||||
export FUNCTION_UVC="uvc.usb0"
|
||||
export FUNCTION_UVC1="uvc1.usb0"
|
||||
export FUNCTION_UAC1="uac1.usb0"
|
||||
|
||||
#-USB Device descriptor.
|
||||
function gen_dev_desc() {
|
||||
echo "0x01" > bDeviceProtocol
|
||||
echo "0x02" > bDeviceSubClass
|
||||
echo "0xEF" > bDeviceClass
|
||||
echo "0x200" > bcdUSB
|
||||
echo $VID > idVendor
|
||||
echo $PID > idProduct
|
||||
echo "0x100" > bcdDevice
|
||||
mkdir -p strings/0x409
|
||||
echo $MANUFACTURER > strings/0x409/manufacturer
|
||||
echo $PRODUCT > strings/0x409/product
|
||||
echo $SERIALNUMBER > strings/0x409/serialnumber
|
||||
}
|
||||
|
||||
#-Payload Format Descriptors
|
||||
function gen_payload_format_desc() {
|
||||
local PAYLOAD=$1
|
||||
local FORMAT=$2
|
||||
local GUID=$3
|
||||
local BITS=$4
|
||||
local RESOLUTIONS=$5
|
||||
local CR=1
|
||||
|
||||
mkdir -p streaming/$PAYLOAD/$FORMAT/
|
||||
|
||||
if [ $GUID != "null" ];then
|
||||
echo -n -e $GUID > streaming/$PAYLOAD/$FORMAT/guidFormat
|
||||
fi
|
||||
|
||||
if [ $BITS -ne 0 ];then
|
||||
echo -n -e $BITS > streaming/$PAYLOAD/$FORMAT/bBitsPerPixel
|
||||
fi
|
||||
|
||||
if [ $FORMAT = "mjpeg" ];then
|
||||
# The compression ratio is calculated at 10:1.
|
||||
CR=10
|
||||
elif [ $FORMAT = "h264" ];then
|
||||
# The compression ratio is calculated at 50:1.
|
||||
CR=50
|
||||
elif [ $FORMAT = "h265" ];then
|
||||
# The compression ratio is calculated at 80:1.
|
||||
CR=100
|
||||
fi
|
||||
|
||||
for str in $RESOLUTIONS; do
|
||||
# Width x Height
|
||||
local RES=$(echo "$str" | awk -F '@' '{print $1}')
|
||||
# Interval
|
||||
local I=$(echo "$str" | awk -F '@' '{print $2}')
|
||||
# Width
|
||||
local W=$(echo "$RES" | awk -F 'x' '{print $1}')
|
||||
# Height
|
||||
local H=$(echo "$RES" | awk -F 'x' '{print $2}')
|
||||
|
||||
RES="${RES}p"
|
||||
|
||||
# microsecond
|
||||
local TIME=10000000
|
||||
# Interval
|
||||
local INTERVAL=$((TIME / I))
|
||||
# Video Frame Buffer Size
|
||||
local FRAMESIZE=$((W * H))
|
||||
if [ $BITS -eq 16 ]; then
|
||||
FRAMESIZE=$((FRAMESIZE * 2))
|
||||
else
|
||||
# The data before encoding is YUV420
|
||||
FRAMESIZE=$((FRAMESIZE * 3))
|
||||
FRAMESIZE=$((FRAMESIZE / 2))
|
||||
fi
|
||||
|
||||
FRAMESIZE=$((FRAMESIZE / CR))
|
||||
|
||||
# Max Bits Rate
|
||||
local MAXBITRATE=$((FRAMESIZE * 8))
|
||||
MAXBITRATE=$((MAXBITRATE * I))
|
||||
# Min Bits Rate
|
||||
local MINBITRATE=$MAXBITRATE
|
||||
|
||||
mkdir -p streaming/$PAYLOAD/$FORMAT/$RES/
|
||||
echo "${W}" > streaming/$PAYLOAD/$FORMAT/$RES/wWidth
|
||||
echo "${H}" > streaming/$PAYLOAD/$FORMAT/$RES/wHeight
|
||||
echo "${INTERVAL}" > streaming/$PAYLOAD/$FORMAT/$RES/dwFrameInterval
|
||||
echo "${INTERVAL}" > streaming/$PAYLOAD/$FORMAT/$RES/dwDefaultFrameInterval
|
||||
echo "${MAXBITRATE}" > streaming/$PAYLOAD/$FORMAT/$RES/dwMaxBitRate
|
||||
echo "${MINBITRATE}" > streaming/$PAYLOAD/$FORMAT/$RES/dwMinBitRate
|
||||
|
||||
if [ $PAYLOAD != "framebased" ];then
|
||||
echo "${FRAMESIZE}" > streaming/$PAYLOAD/$FORMAT/$RES/dwMaxVideoFrameBufferSize
|
||||
fi
|
||||
|
||||
#echo "$FORMAT $W x $H @ $INTERVAL $FRAMESIZE $MAXBITRATE"
|
||||
done
|
||||
}
|
||||
|
||||
#-For YUV Payload Format Descriptor
|
||||
function gen_uncompressed_desc() {
|
||||
local PAYLOAD="uncompressed"
|
||||
local FORMAT=$1
|
||||
local GUID=$2
|
||||
local BITS=$3
|
||||
local RESOLUTIONS=$4
|
||||
|
||||
gen_payload_format_desc ${PAYLOAD} ${FORMAT} ${GUID} ${BITS} "${RESOLUTIONS}"
|
||||
|
||||
ln -s streaming/$PAYLOAD/$FORMAT/ streaming/header/h/
|
||||
}
|
||||
|
||||
#-For FRAMEBASE Payload Format Descriptor
|
||||
function gen_framebase_desc() {
|
||||
local PAYLOAD="framebased"
|
||||
local FORMAT=$1
|
||||
local GUID=$2
|
||||
local BITS=0
|
||||
local RESOLUTIONS=$3
|
||||
|
||||
gen_payload_format_desc ${PAYLOAD} ${FORMAT} ${GUID} ${BITS} "${RESOLUTIONS}"
|
||||
|
||||
ln -s streaming/$PAYLOAD/$FORMAT/ streaming/header/h/
|
||||
}
|
||||
|
||||
#-For MJPEG Payload Format Descriptor
|
||||
function __gen_mjpeg_desc() {
|
||||
local PAYLOAD="mjpeg"
|
||||
local FORMAT="mjpeg"
|
||||
local GUID="null"
|
||||
local BITS=0
|
||||
local RESOLUTIONS=$1
|
||||
|
||||
gen_payload_format_desc ${PAYLOAD} ${FORMAT} ${GUID} ${BITS} "${RESOLUTIONS}"
|
||||
|
||||
ln -s streaming/$PAYLOAD/$FORMAT/ streaming/header/h/
|
||||
}
|
||||
|
||||
#-YUYV Format
|
||||
function gen_yuyv_desc() {
|
||||
gen_uncompressed_desc "yuyv" "${YUYV_GUID}" "${YUYV_BITS}" "${YUYV}"
|
||||
}
|
||||
|
||||
#-NV21 Format
|
||||
function gen_nv21_desc() {
|
||||
gen_uncompressed_desc "nv21" "${NV21_GUID}" "${NV21_BITS}" "${NV21}"
|
||||
}
|
||||
|
||||
#-H264 Format
|
||||
function gen_h264_desc() {
|
||||
gen_framebase_desc "h264" "${H264_GUID}" "${H264}"
|
||||
}
|
||||
|
||||
#-H265 Format
|
||||
function gen_h265_desc() {
|
||||
gen_framebase_desc "h265" "${H265_GUID}" "${H265}"
|
||||
}
|
||||
|
||||
#-MJPEG Format
|
||||
function gen_mjpeg_desc() {
|
||||
__gen_mjpeg_desc "${MJPEG}"
|
||||
}
|
||||
|
||||
#-UVC common
|
||||
function __gen_uvc_desc() {
|
||||
local FUNNAME=$1
|
||||
|
||||
mkdir -p functions/$FUNNAME
|
||||
cd functions/$FUNNAME
|
||||
|
||||
# streaming_maxpacket: 1 ~ 3072
|
||||
# high-speed and super-speed: (1 ~ 3072); full-speed: (1 ~ 1023)
|
||||
if [ $UVC_TRANSFER_MODE = "isoc" ];then
|
||||
# USB ISOC transfer mode
|
||||
echo 0 > streaming_bulk
|
||||
echo 1 > streaming_interval
|
||||
|
||||
if [ $USB_SPEED = "fs" ];then
|
||||
# USB full-speed
|
||||
echo 0 > streaming_maxburst
|
||||
echo 512 > streaming_maxpacket
|
||||
elif [ $USB_SPEED = "hs" ];then
|
||||
# USB high-speed
|
||||
echo 0 > streaming_maxburst
|
||||
echo 3072 > streaming_maxpacket
|
||||
elif [ $USB_SPEED = "ss" ];then
|
||||
# USB super-speed
|
||||
echo 9 > streaming_maxburst
|
||||
echo 3072 > streaming_maxpacket
|
||||
fi
|
||||
else
|
||||
# USB BULK transfer mode
|
||||
echo 1 > streaming_bulk
|
||||
echo 0 > streaming_interval
|
||||
|
||||
if [ $USB_SPEED = "fs" ];then
|
||||
# USB full-speed
|
||||
echo 0 > streaming_maxburst
|
||||
echo 64 > streaming_maxpacket
|
||||
elif [ $USB_SPEED = "hs" ];then
|
||||
# USB high-speed
|
||||
echo 0 > streaming_maxburst
|
||||
echo 512 > streaming_maxpacket
|
||||
elif [ $USB_SPEED = "ss" ];then
|
||||
# USB super-speed
|
||||
echo 9 > streaming_maxburst
|
||||
echo 1024 > streaming_maxpacket
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p control/header/h/
|
||||
mkdir -p streaming/header/h/
|
||||
|
||||
echo "0x0110" > control/header/h/bcdUVC
|
||||
echo "96000000" > control/header/h/dwClockFrequency
|
||||
|
||||
ln -s control/header/h/ control/class/fs/
|
||||
ln -s control/header/h/ control/class/ss/
|
||||
|
||||
#YUYV
|
||||
if [ $UVC_ENABLE_YUYV = "true" ];then
|
||||
gen_yuyv_desc
|
||||
fi
|
||||
|
||||
#NV21
|
||||
if [ $UVC_ENABLE_NV21 = "true" ];then
|
||||
gen_nv21_desc
|
||||
fi
|
||||
|
||||
#MJPEG
|
||||
if [ $UVC_ENABLE_MJPEG = "true" ];then
|
||||
gen_mjpeg_desc
|
||||
fi
|
||||
|
||||
#H264
|
||||
if [ $UVC_ENABLE_H264 = "true" ];then
|
||||
gen_h264_desc
|
||||
fi
|
||||
|
||||
#H265
|
||||
if [ $UVC_ENABLE_H265 = "true" ];then
|
||||
gen_h265_desc
|
||||
fi
|
||||
|
||||
ln -s streaming/header/h/ streaming/class/fs/
|
||||
ln -s streaming/header/h/ streaming/class/hs/
|
||||
ln -s streaming/header/h/ streaming/class/ss/
|
||||
|
||||
cd ../../
|
||||
}
|
||||
|
||||
#-UAC common
|
||||
function __gen_uac_desc() {
|
||||
local FUNNAME=$1
|
||||
|
||||
mkdir -p functions/$FUNNAME
|
||||
echo "0x01" > functions/$FUNNAME/c_chmask
|
||||
echo "16000" > functions/$FUNNAME/c_srate
|
||||
|
||||
echo "0x01" > functions/$FUNNAME/p_chmask
|
||||
echo "16000" > functions/$FUNNAME/p_srate
|
||||
}
|
||||
|
||||
#-UVC
|
||||
function gen_uvc_desc() {
|
||||
__gen_uvc_desc "${FUNCTION_UVC}"
|
||||
}
|
||||
|
||||
#-UVC1
|
||||
function gen_uvc1_desc() {
|
||||
__gen_uvc_desc "${FUNCTION_UVC1}"
|
||||
}
|
||||
|
||||
#-UAC1
|
||||
function gen_uac1_desc() {
|
||||
__gen_uac_desc "${FUNCTION_UAC1}"
|
||||
}
|
||||
|
||||
################################################################################################
|
||||
|
||||
# Main
|
||||
APP_PATH=$PWD
|
||||
|
||||
# Step 1
|
||||
mount -t configfs none /sys/kernel/config/
|
||||
cd /sys/kernel/config/usb_gadget/
|
||||
mkdir -p camera
|
||||
cd camera
|
||||
|
||||
# Step 2
|
||||
gen_dev_desc
|
||||
|
||||
# Step 3
|
||||
if [ $UVC_ENABLE = true ];then
|
||||
gen_uvc_desc
|
||||
fi
|
||||
|
||||
if [ $UVC1_ENABLE = true ];then
|
||||
gen_uvc1_desc
|
||||
fi
|
||||
|
||||
if [ $UAC_ENABLE = true ];then
|
||||
gen_uac1_desc
|
||||
fi
|
||||
|
||||
# Step 4
|
||||
#-Create and setup configuration
|
||||
mkdir -p configs/c.1/
|
||||
echo "500" > configs/c.1/MaxPower
|
||||
echo "0x80" > configs/c.1/bmAttributes
|
||||
mkdir -p configs/c.1/strings/0x409/
|
||||
echo "Config 1" > configs/c.1/strings/0x409/configuration
|
||||
|
||||
# Step 5
|
||||
if [ $UVC_ENABLE = true ];then
|
||||
ln -s functions/${FUNCTION_UVC}/ configs/c.1/
|
||||
fi
|
||||
|
||||
if [ $UVC1_ENABLE = true ];then
|
||||
ln -s functions/${FUNCTION_UVC1}/ configs/c.1/
|
||||
fi
|
||||
|
||||
if [ $UAC_ENABLE = true ];then
|
||||
ln -s functions/${FUNCTION_UAC1}/ configs/c.1/
|
||||
fi
|
||||
|
||||
# Step 6
|
||||
dwc=$(ls /sys/class/udc)
|
||||
echo "${dwc}" > UDC
|
||||
|
||||
# Step 7
|
||||
#cd $APP_PATH
|
||||
#./uvc_app
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "" > /sys/kernel/config/usb_gadget/camera/UDC
|
||||
unlink /sys/kernel/config/usb_gadget/camera/configs/c.1/uvc.usb0
|
||||
rm -rf /sys/kernel/config/usb_gadget/camera/configs/c.1/ 2>>/dev/null
|
||||
rm -rf /sys/kernel/config/usb_gadget/camera/functions/uvc.usb0 2>>/dev/null
|
||||
rm -rf /sys/kernel/config/usb_gadget/camera 2>>/dev/null
|
||||
umount /sys/kernel/config/
|
||||
#rmmod usb_f_fs
|
||||
#rmmod usb_f_uvc
|
||||
#rmmod libcomposite
|
||||
@@ -0,0 +1,96 @@
|
||||
ifeq ($(CFG_XMEDIA_EXPORT_FLAG),)
|
||||
SDK_DIR := $(shell cd $(CURDIR)/../.. && /bin/pwd)
|
||||
endif
|
||||
|
||||
include $(SDK_DIR)/build/base.mk
|
||||
include $(SAMPLE_DIR)/sample_base.mk
|
||||
|
||||
CUR_DIR := $(shell /bin/pwd)
|
||||
|
||||
TARGET := uvc_app
|
||||
|
||||
UVC_ENABLE := true
|
||||
UAC_ENABLE := false
|
||||
|
||||
INCLUDES := $(SAMPLE_INCLUDES)
|
||||
|
||||
INCLUDES += -I$(CUR_DIR)
|
||||
INCLUDES += -I$(CUR_DIR)/debug
|
||||
INCLUDES += -I$(CUR_DIR)/common
|
||||
|
||||
APP_CFLAGS :=
|
||||
#APP_CFLAGS += -DPRINT_COLOR_ON=1
|
||||
#APP_CFLAGS += -DDUMP_STREAM_DATA=1
|
||||
|
||||
SRCS := $(wildcard ./*.c)
|
||||
SRCS += $(wildcard ./common/*.c)
|
||||
SRCS += $(wildcard ./debug/*.c)
|
||||
|
||||
ifeq ($(UVC_ENABLE),true)
|
||||
APP_CFLAGS += -DUVC_COMPILE=1
|
||||
INCLUDES += -I$(CUR_DIR)/video
|
||||
INCLUDES += -I$(CUR_DIR)/video/hal
|
||||
INCLUDES += -I$(CUR_DIR)/video/stream
|
||||
INCLUDES += -I$(CUR_DIR)/video/hal/uapi
|
||||
|
||||
SRCS += $(wildcard ./video/*.c)
|
||||
SRCS += $(wildcard ./video/hal/*.c)
|
||||
#SRCS += $(wildcard ./video/stream/*.c)
|
||||
SRCS += video/stream/video_stream.c
|
||||
SRCS += video/stream/sample_video.c
|
||||
SRCS += video/stream/sample_video_h264.c
|
||||
SRCS += video/stream/sample_video_mjpeg.c
|
||||
SRCS += video/stream/sample_video_yuv.c
|
||||
#SRCS += video/stream/sample_video_mjpeg_h264.c
|
||||
#SRCS += video/stream/memcpy_jpeg.c
|
||||
#SRCS += video/stream/pack_h264_to_jpeg.c
|
||||
endif
|
||||
|
||||
ifeq ($(UAC_ENABLE),true)
|
||||
APP_CFLAGS += -DUAC_COMPILE=1
|
||||
INCLUDES += -I$(CUR_DIR)/audio
|
||||
INCLUDES += -I$(CUR_DIR)/audio/hal
|
||||
INCLUDES += -I$(CUR_DIR)/audio/stream
|
||||
INCLUDES += -I$(CUR_DIR)/audio/alsa/install/include
|
||||
|
||||
APP_CFLAGS += -L$(CUR_DIR)/audio/alsa/install/lib
|
||||
|
||||
SRCS += $(wildcard ./audio/*.c)
|
||||
SRCS += $(wildcard ./audio/hal/*.c)
|
||||
SRCS += $(wildcard ./audio/stream/*.c)
|
||||
|
||||
GMP_LIBS += -lasound
|
||||
endif
|
||||
|
||||
LIBS := $(SAMPLE_LIBS) $(SAMPLE_COMMON_LIB)
|
||||
|
||||
CFLAGS := $(SAMPLE_CFLAGS) $(APP_CFLAGS) $(LIBS) $(INCLUDES)
|
||||
|
||||
OBJS := $(patsubst %.c, %.o, $(SRCS))
|
||||
|
||||
.PHONY: all clean prepare
|
||||
|
||||
all: prepare target
|
||||
|
||||
clean:
|
||||
$(AT)rm -rf $(OBJS) $(TARGET)
|
||||
|
||||
target: $(OBJS)
|
||||
$(AT)$(CC) -o $(TARGET) $^ $(CFLAGS)
|
||||
|
||||
%.o : %.c
|
||||
$(AT)$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
||||
$(OBJS): prepare
|
||||
|
||||
ifeq ($(UAC_ENABLE),true)
|
||||
prepare:
|
||||
make -C audio/alsa all
|
||||
|
||||
distclean: clean
|
||||
make -C audio/alsa clean
|
||||
else
|
||||
prepare:
|
||||
|
||||
endif
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
This document briefly describes how to port and use alsa-lib.
|
||||
|
||||
1.download alsa-lib source code
|
||||
Note: The alsa-lib source package is not released by default, only dynamic library files are released. The alsa-lib source package needs to be downloaded from the open source community.
|
||||
Download the source code of alsa-lib v1.1.7 from the alsa-project open source community:
|
||||
1) Go to the website: www.alsa-project.org
|
||||
2) Select the http://www.alsa-project.org/main/index.php/Download option of the HTTP protocol resource to enter the subpage
|
||||
3) Download alsa-lib-1.1.7.tar.bz2
|
||||
|
||||
2.compile alsa-lib
|
||||
1) Store the downloaded alsa-lib-1.1.7.tar.bz2 in the sdk/sample/uvc_app/audio/alsa/opensource directory.
|
||||
2) In the linux server, enter the sdk/sample/uvc_app/audio/alsa/opensource directory and execute the following command:
|
||||
tar -xjvf alsa-lib-1.1.7.tar.bz2
|
||||
cd ./alsa-lib-1.1.7/
|
||||
mkdir -p /home/install/alsa-lib-1.1.7/
|
||||
./configure CC=arm-gcc12.2.0-linux-gcc STRIP=arm-gcc12.2.0-linux-strip --host=arm-gcc12.2.0-linux --prefix=/home/install/alsa-lib-1.1.7/ --enable-static=no --enable-shared=yes --with-configdir=/home/audio/alsa/ --disable-python
|
||||
make
|
||||
make install
|
||||
cp -r /home/install/alsa-lib-1.1.7/lib/ ../../
|
||||
cp -r /home/install/alsa-lib-1.1.7/include/ ../../
|
||||
|
||||
Note: The above command is used to generate the dynamic link library of alsa-lib. --host is used to specify the cross compiler. --prefix is used to specify the installation path of the compiled file. --enable-static is used to specify static library support. --enable-shared is used to specify dynamic library support. --with-configdir is used to specify the installation path of the config file (also the path of the config file on the board).
|
||||
|
||||
3.install to the embedded platform
|
||||
On the target board, the following files need to be copied to the corresponding location:
|
||||
1) Copy the libasound.so.2 file in the /home/install/alsa-lib-1.1.7/lib/ directory on the server to the /lib/ directory on the board.
|
||||
2) Copy all the files in the /home/audio/alsa/ directory on the server to the /home/audio/alsa/ directory of the board (create it if it does not exist).
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __UVC_APP_LOG_H__
|
||||
#define __UVC_APP_LOG_H__
|
||||
|
||||
#include "log_base.h"
|
||||
|
||||
#undef TAG
|
||||
#define TAG "UVC_APP"
|
||||
|
||||
#define app_loge LOGE
|
||||
#define app_logw LOGW
|
||||
#define app_logi LOGI
|
||||
#define app_logt LOGT
|
||||
#define app_logd LOGD
|
||||
|
||||
#define check_null_goto(ptr, tag) do { \
|
||||
if ((ptr) == NULL) { \
|
||||
app_loge("invalid param.\n"); \
|
||||
goto tag; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "camera.h"
|
||||
#include "frame_cache.h"
|
||||
#include "config_svc.h"
|
||||
|
||||
#include "log_base.h"
|
||||
|
||||
unsigned int g_uvc = 1;
|
||||
unsigned int g_bulk = 0;
|
||||
unsigned int g_uac = 0;
|
||||
unsigned int g_loglevel = LOG_LEVEL_INFO;
|
||||
|
||||
volatile sig_atomic_t g_need_quit_flag = 0;
|
||||
|
||||
static void sample_uvc_usage(char *process)
|
||||
{
|
||||
printf("Usage : %s <param>\n", process);
|
||||
printf("param:\n");
|
||||
printf("\t -h --for help.\n");
|
||||
printf("\t -bulkmode --use uvc bulkmode.\n");
|
||||
printf("\t -uac --enable uac.\n");
|
||||
printf("\t -loglevel x --set the log level to x (0 ~ 5).\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void sample_uvc_handle_signal(int signo)
|
||||
{
|
||||
|
||||
switch (signo) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
signal(SIGINT, SIG_IGN);
|
||||
signal(SIGTERM, SIG_IGN);
|
||||
signal(SIGUSR1, SIG_IGN);
|
||||
signal(SIGUSR2, SIG_IGN);
|
||||
g_need_quit_flag = 1;
|
||||
return;
|
||||
|
||||
case SIGUSR1:
|
||||
if (g_loglevel > 0)
|
||||
g_loglevel--;
|
||||
break;
|
||||
|
||||
case SIGUSR2:
|
||||
if (g_loglevel < LOG_LEVEL_DEBUG)
|
||||
g_loglevel++;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
printf("[UVC_APP] Change the log level to %d\n", g_loglevel);
|
||||
}
|
||||
|
||||
static int create_cache(void)
|
||||
{
|
||||
if (create_uvc_cache() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void destroy_cache(void)
|
||||
{
|
||||
destroy_uvc_cache();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int run_camera(void)
|
||||
{
|
||||
if (get_camera()->init() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (get_camera()->open() != 0) {
|
||||
get_camera()->deinit();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (get_camera()->run() != 0) {
|
||||
get_camera()->close();
|
||||
get_camera()->deinit();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void stop_camera(void)
|
||||
{
|
||||
get_camera()->stop();
|
||||
get_camera()->close();
|
||||
get_camera()->deinit();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i = 1;
|
||||
|
||||
printf("\n@@@@@ UVC App Sample @@@@@\n\n");
|
||||
|
||||
while (i < argc) {
|
||||
if (strcmp(argv[i], "-bulkmode") == 0) {
|
||||
g_bulk = 1;
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "-uac") == 0) {
|
||||
g_uac = 1;
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "-loglevel") == 0) {
|
||||
i++;
|
||||
g_loglevel = atoi(argv[i]);
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "-h") == 0) {
|
||||
sample_uvc_usage(argv[0]);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
signal(SIGINT, sample_uvc_handle_signal);
|
||||
signal(SIGTERM, sample_uvc_handle_signal);
|
||||
signal(SIGUSR1, sample_uvc_handle_signal);
|
||||
signal(SIGUSR2, sample_uvc_handle_signal);
|
||||
|
||||
if (create_config_svc("./uvc_app.conf") != 0) {
|
||||
goto EXIT0;
|
||||
}
|
||||
|
||||
if (create_cache() != 0) {
|
||||
goto EXIT1;
|
||||
}
|
||||
|
||||
if (run_camera() != 0) {
|
||||
goto EXIT2;
|
||||
}
|
||||
|
||||
while(!g_need_quit_flag) {
|
||||
sleep(2);
|
||||
}
|
||||
|
||||
EXIT2:
|
||||
stop_camera();
|
||||
destroy_cache();
|
||||
|
||||
EXIT1:
|
||||
release_cofnig_svc();
|
||||
|
||||
EXIT0:
|
||||
printf("uvc_app exit!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env make
|
||||
###############################################################################
|
||||
#
|
||||
# Copyright (c) XMEDIA. All rights reserved.
|
||||
#
|
||||
###############################################################################
|
||||
ifeq ($(CFG_XMEDIA_EXPORT_FLAG),)
|
||||
SDK_DIR := $(shell cd $(CURDIR)/../../../../ && /bin/pwd)
|
||||
endif
|
||||
|
||||
include $(SDK_DIR)/build/base.mk
|
||||
|
||||
USER := $(shell whoami)
|
||||
|
||||
CROSS_COMPILE:=$(TOOLCHAIN)
|
||||
|
||||
ALSALIB := alsa-lib-1.1.7
|
||||
|
||||
ALSALIB_BUILDDIR = $(SDK_DIR)/sample/uvc_app/audio/alsa
|
||||
|
||||
ALSALIB_INSTALLDIR = $(ALSALIB_BUILDDIR)/install
|
||||
|
||||
ALSALIB_PREFIX = $(ALSALIB_BUILDDIR)/opensource/$(ALSALIB)/.install
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all:$(ALSALIB_BUILDDIR)/install/lib/libasound.a
|
||||
|
||||
clean: $(ALSALIB).clean
|
||||
|
||||
$(ALSALIB).clean:
|
||||
rm -rf $(ALSALIB_BUILDDIR)/opensource/$(ALSALIB)
|
||||
rm -rf $(ALSALIB_BUILDDIR)/install
|
||||
|
||||
$(ALSALIB_BUILDDIR)/install/lib/libasound.a: $(ALSALIB_BUILDDIR)/opensource/$(ALSALIB)/.built
|
||||
touch $@
|
||||
|
||||
$(ALSALIB_BUILDDIR)/opensource/$(ALSALIB)/.built: $(ALSALIB_BUILDDIR)/opensource/$(ALSALIB)/.extracted
|
||||
cd $(<D); CC=$(CROSS_COMPILE)-gcc STRIP=$(CROSS_COMPILE)-strip ./configure \
|
||||
--prefix=$(ALSALIB_INSTALLDIR) \
|
||||
--host=$(CROSS_COMPILE) \
|
||||
--enable-static=yes \
|
||||
--enable-shared=no \
|
||||
--disable-python \
|
||||
--with-configdir=/home/$(USER)/audio/alsa
|
||||
make -C $(<D)
|
||||
make -C $(<D) install
|
||||
touch $@
|
||||
|
||||
$(ALSALIB_BUILDDIR)/opensource/$(ALSALIB)/.extracted:
|
||||
tar -xjvf $(ALSALIB_BUILDDIR)/opensource/$(ALSALIB).tar.bz2 -C $(ALSALIB_BUILDDIR)/opensource
|
||||
touch $@
|
||||
@@ -0,0 +1,815 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include "uac_hal.h"
|
||||
#include "uac_log.h"
|
||||
|
||||
#define UAC_HOST_END 0
|
||||
#define PCM_WAIT_TIME_MS 1000
|
||||
|
||||
struct data_buffer {
|
||||
char* addr;
|
||||
unsigned int size; // The unit is bytes, and the size is equal to period_stize.
|
||||
};
|
||||
|
||||
struct user_args {
|
||||
char id[16];
|
||||
unsigned int rate;
|
||||
unsigned int channels;
|
||||
unsigned int buffer_time;
|
||||
unsigned int period_time;
|
||||
snd_pcm_format_t format;
|
||||
snd_pcm_sframes_t buffer_size; // number of samples, When calculating memory space, it is necessary to convert it into bytes.
|
||||
snd_pcm_sframes_t period_size; // number of samples, When calculating memory space, it is necessary to convert it into bytes.
|
||||
};
|
||||
|
||||
static struct data_buffer g_playback_data = {0};
|
||||
static snd_pcm_t* g_handle_playback = NULL;
|
||||
static struct user_args p_args = {
|
||||
.id = "playback", /* playback */
|
||||
.rate = 16000, /* stream rate */
|
||||
.channels = 1, /* count of channels */
|
||||
.buffer_time = 40000, /* ring buffer length in us */
|
||||
.period_time = 10000, /* period time in us */
|
||||
.format = SND_PCM_FORMAT_S16_LE, /* sample format */
|
||||
};
|
||||
|
||||
static struct data_buffer g_capture_data = {0};
|
||||
static snd_pcm_t* g_handle_capture = NULL;
|
||||
static struct user_args c_args = {
|
||||
.id = "capture", /* capture */
|
||||
.rate = 16000, /* stream rate */
|
||||
.channels = 1, /* count of channels */
|
||||
.buffer_time = 40000, /* ring buffer length in us */
|
||||
.period_time = 10000, /* period time in us */
|
||||
.format = SND_PCM_FORMAT_S16_LE, /* sample format */
|
||||
};
|
||||
|
||||
#if UAC_HOST_END
|
||||
static char *device = "hw:0,0"; /* playback and capture device */
|
||||
#else
|
||||
static char *device = "default"; /* playback and capture device */
|
||||
#endif
|
||||
|
||||
static int g_playback_run = 0;
|
||||
static int g_capture_run = 0;
|
||||
|
||||
static audio_stream_operate_t* uac_stream = NULL;
|
||||
|
||||
/******************************* UAC Stream *******************************************/
|
||||
|
||||
static int uac_stream_config_playback(unsigned int channels,
|
||||
unsigned int rate,
|
||||
unsigned int period_time,
|
||||
audio_stream_format_t format,
|
||||
int is_interleaved)
|
||||
{
|
||||
if (uac_stream && uac_stream->playback_stream && uac_stream->playback_stream->config) {
|
||||
return uac_stream->playback_stream->config(channels, rate, period_time, format, is_interleaved);
|
||||
} else {
|
||||
uac_loge("Invalid config function for playback stream.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int uac_stream_startup_playback(void)
|
||||
{
|
||||
if (uac_stream && uac_stream->playback_stream && uac_stream->playback_stream->startup) {
|
||||
return uac_stream->playback_stream->startup();
|
||||
} else {
|
||||
uac_loge("Invalid startup function for playback stream.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int uac_stream_shutdown_playback(void)
|
||||
{
|
||||
if (uac_stream && uac_stream->playback_stream && uac_stream->playback_stream->shutdown) {
|
||||
return uac_stream->playback_stream->shutdown();
|
||||
} else {
|
||||
uac_loge("Invalid shutdown function for playback stream.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int uac_stream_recv_from_ai(char* buf, int size, int timeout_ms) {
|
||||
if (uac_stream && uac_stream->playback_stream
|
||||
&& uac_stream->playback_stream->recv_from_stream) {
|
||||
return uac_stream->playback_stream->recv_from_stream(buf, size, timeout_ms);
|
||||
} else {
|
||||
uac_loge("Invalid recv function for playback stream.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int uac_stream_config_capture(unsigned int channels,
|
||||
unsigned int rate,
|
||||
unsigned int period_time,
|
||||
audio_stream_format_t format,
|
||||
int is_interleaved)
|
||||
{
|
||||
if (uac_stream && uac_stream->capture_stream && uac_stream->capture_stream->config) {
|
||||
return uac_stream->capture_stream->config(channels, rate, period_time, format, is_interleaved);
|
||||
} else {
|
||||
uac_loge("Invalid contig function for capture stream.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int uac_stream_startup_capture(void)
|
||||
{
|
||||
if (uac_stream && uac_stream->capture_stream && uac_stream->capture_stream->startup) {
|
||||
return uac_stream->capture_stream->startup();
|
||||
} else {
|
||||
uac_loge("Invalid startup function for capture stream.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int uac_stream_shutdown_capture(void)
|
||||
{
|
||||
if (uac_stream && uac_stream->capture_stream && uac_stream->capture_stream->shutdown) {
|
||||
return uac_stream->capture_stream->shutdown();
|
||||
} else {
|
||||
uac_loge("Invalid shutdown function for capture.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int uac_stream_send_to_ao(char* buf, int size, int timeout_ms)
|
||||
{
|
||||
if (uac_stream && uac_stream->capture_stream
|
||||
&& uac_stream->capture_stream->send_to_stream) {
|
||||
return uac_stream->capture_stream->send_to_stream(buf, size, timeout_ms);
|
||||
} else {
|
||||
uac_loge("Invalid send function for capture stream.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************************/
|
||||
|
||||
/******************************* UAC Device *******************************************/
|
||||
static int set_hwparams(snd_pcm_t* handle, snd_pcm_hw_params_t* params, struct user_args* args)
|
||||
{
|
||||
int err;
|
||||
unsigned int val, sample_rate;
|
||||
unsigned int channels = args->channels;
|
||||
char* id = (char*)args->id;
|
||||
snd_pcm_format_t format = args->format;
|
||||
snd_pcm_uframes_t frames = 0;
|
||||
|
||||
/* Fill it in with default values. */
|
||||
err = snd_pcm_hw_params_any(handle, params);
|
||||
if (err < 0) {
|
||||
uac_loge("Broken configuration for %s: no configurations available: %s\n", id, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
/* set the interleaved read/write format */
|
||||
err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
if (err < 0) {
|
||||
uac_loge("Access type not available for %s: %s\n", id, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
/* set the sample format */
|
||||
err = snd_pcm_hw_params_set_format(handle, params, format);
|
||||
if (err < 0) {
|
||||
uac_loge("Sample format not available for %s: %s\n", id, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
/* set the count of channels */
|
||||
err = snd_pcm_hw_params_set_channels(handle, params, channels);
|
||||
if (err < 0) {
|
||||
uac_loge("Channels count (%i) not available for %s: %s\n", channels, id, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
/* set the stream rate */
|
||||
val = args->rate;
|
||||
err = snd_pcm_hw_params_set_rate_near(handle, params, &val, 0);
|
||||
if (err < 0) {
|
||||
uac_loge("Rate %iHz not available for %s: %s\n", args->rate, id, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
if (val != args->rate) {
|
||||
uac_logw("Rate doesn't match for %s: (requested %iHz, get %iHz)\n", id, args->rate, val);
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_get_rate(params, &sample_rate, 0);
|
||||
if (err < 0) {
|
||||
uac_loge("Unable to get rate for %s: %s.\n", id, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
args->rate = sample_rate;
|
||||
|
||||
/* set set the period size */
|
||||
frames = sample_rate / 1000 * (args->period_time / 1000);
|
||||
err = snd_pcm_hw_params_set_period_size(handle, params, frames, 0);
|
||||
if (err < 0) {
|
||||
uac_loge("Unable to set period size %lu for %s: %s.\n", frames, id, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_get_period_size(params, &frames, NULL);
|
||||
if (err < 0) {
|
||||
uac_loge("Unable to get period size for %s: %s.\n", id, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
args->period_size = frames;
|
||||
|
||||
/* set the buffer size */
|
||||
frames = (args->buffer_time / args->period_time) * args->period_size;
|
||||
err = snd_pcm_hw_params_set_buffer_size(handle, params, frames);
|
||||
if (err < 0) {
|
||||
uac_loge("Unable to set buffer size %lu for %s: %s.\n", frames, id, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_get_buffer_size(params, &frames);
|
||||
if (err < 0) {
|
||||
uac_loge("Unable to get buffer size for %s: %s.\n", id, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
args->buffer_size = frames;
|
||||
|
||||
/* write the parameters to device */
|
||||
err = snd_pcm_hw_params(handle, params);
|
||||
if (err < 0) {
|
||||
uac_loge("Unable to set hw params for %s: %s\n", id, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// For playback
|
||||
static int set_swparams(snd_pcm_t* handle, snd_pcm_sw_params_t* swparams, struct user_args* args)
|
||||
{
|
||||
int err;
|
||||
int period_event = 0; /* produce poll event after each period */
|
||||
char* id = (char*)args->id;
|
||||
snd_pcm_sframes_t buffer_size = args->buffer_size;
|
||||
snd_pcm_sframes_t period_size = args->period_size;
|
||||
|
||||
|
||||
/* get the current swparams */
|
||||
err = snd_pcm_sw_params_current(handle, swparams);
|
||||
if (err < 0) {
|
||||
uac_loge("Unable to determine current swparams for %s: %s\n", id, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
/* start the transfer when the buffer is almost full: */
|
||||
/* (buffer_size / avail_min) * avail_min */
|
||||
err = snd_pcm_sw_params_set_start_threshold(handle, swparams, (buffer_size / period_size) * period_size);
|
||||
if (err < 0) {
|
||||
uac_loge("Unable to set start threshold mode for %s: %s\n", id, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
/* allow the transfer when at least period_size samples can be processed */
|
||||
/* or disable this mechanism when period event is enabled (aka interrupt like style processing) */
|
||||
err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_event ? buffer_size : period_size);
|
||||
if (err < 0) {
|
||||
uac_loge("Unable to set avail min for %s: %s\n", id, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
/* enable period events when requested */
|
||||
if (period_event) {
|
||||
err = snd_pcm_sw_params_set_period_event(handle, swparams, 1);
|
||||
if (err < 0) {
|
||||
uac_loge("Unable to set period event %s: %s\n", id, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
/* write the parameters to the playback device */
|
||||
err = snd_pcm_sw_params(handle, swparams);
|
||||
if (err < 0) {
|
||||
uac_loge("Unable to set sw params for %s: %s\n", id, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alsa_playback_init(void)
|
||||
{
|
||||
// init p_args
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alsa_playback_deinit(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alsa_playback_open(void)
|
||||
{
|
||||
int err;
|
||||
int buf_size;
|
||||
snd_output_t *output = NULL;
|
||||
snd_pcm_hw_params_t *hwparams = NULL;
|
||||
snd_pcm_sw_params_t *swparams = NULL;
|
||||
|
||||
err = snd_output_stdio_attach(&output, stdout, 0);
|
||||
if (err < 0) {
|
||||
uac_logw("Output failed: %s\n", snd_strerror(err));
|
||||
}
|
||||
|
||||
err = snd_pcm_open(&g_handle_playback, device, SND_PCM_STREAM_PLAYBACK, 0);
|
||||
if (err < 0) {
|
||||
uac_loge("snd_pcm_open playback failed: %s.\n", snd_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
snd_pcm_hw_params_alloca(&hwparams);
|
||||
snd_pcm_sw_params_alloca(&swparams);
|
||||
|
||||
err = set_hwparams(g_handle_playback, hwparams, &p_args);
|
||||
if (err < 0) {
|
||||
goto hw_err;
|
||||
}
|
||||
|
||||
err = set_swparams(g_handle_playback, swparams, &p_args);
|
||||
if (err < 0) {
|
||||
goto hw_err;
|
||||
}
|
||||
|
||||
// p_args.period_size : Number of samples per cycle
|
||||
// (p_args.channels * snd_pcm_format_physical_width(p_args.format) / 8) : The number of bytes per sample.
|
||||
buf_size = p_args.period_size * (p_args.channels * snd_pcm_format_physical_width(p_args.format) / 8);
|
||||
g_playback_data.addr = malloc(buf_size);
|
||||
if (g_playback_data.addr == NULL) {
|
||||
uac_loge("No enough memory\n");
|
||||
goto sw_err;
|
||||
}
|
||||
|
||||
g_playback_data.size = buf_size;
|
||||
|
||||
uac_stream_config_playback(p_args.channels,
|
||||
p_args.rate,
|
||||
p_args.period_time / 1000,
|
||||
AUDIO_STREAM_FORMAT_S16_LE,
|
||||
1);
|
||||
|
||||
|
||||
/* show the PCM setup parameters */
|
||||
snd_pcm_dump(g_handle_playback, output);
|
||||
|
||||
return 0;
|
||||
|
||||
hw_err:
|
||||
sw_err:
|
||||
snd_pcm_close(g_handle_playback);
|
||||
g_handle_playback = NULL;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int alsa_playback_close(void)
|
||||
{
|
||||
if (g_playback_data.addr != NULL) {
|
||||
free(g_playback_data.addr);
|
||||
g_playback_data.addr = NULL;
|
||||
}
|
||||
|
||||
if (g_handle_playback != NULL) {
|
||||
snd_pcm_close(g_handle_playback);
|
||||
g_handle_playback = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Recv audio frame from AI, and send to ALSA */
|
||||
static int alsa_playback_run(int timeout_ms)
|
||||
{
|
||||
int err, cptr;
|
||||
char* buffer = g_playback_data.addr;
|
||||
unsigned int buf_len = g_playback_data.size;
|
||||
unsigned int period_size = p_args.period_size;
|
||||
snd_pcm_t* handle = g_handle_playback;
|
||||
|
||||
uac_logd("Playback stream start\n");
|
||||
memset(buffer, 0, buf_len);
|
||||
err = uac_stream_recv_from_ai(buffer, buf_len, timeout_ms);
|
||||
if (err < 0) {
|
||||
uac_loge("recv stream from ai failed.\n");
|
||||
//return -1;
|
||||
}
|
||||
|
||||
cptr = period_size;
|
||||
while (cptr > 0 && g_playback_run) {
|
||||
err = snd_pcm_wait(handle, timeout_ms);
|
||||
if (err == 0) {
|
||||
// If the other end keeps not taking data, the buffer will be continuously timed out after being consumed.
|
||||
uac_logd("Playback pcm wait timeout(%dms)\n", timeout_ms);
|
||||
break;
|
||||
}
|
||||
|
||||
err = snd_pcm_writei(handle, buffer, cptr);
|
||||
#if 1
|
||||
if (err == -EPIPE) {
|
||||
/* EPIPE means underrun */
|
||||
uac_loge("Playback underrun occurred, %s\n", snd_strerror(err));
|
||||
snd_pcm_prepare(handle);
|
||||
snd_pcm_start(handle);
|
||||
continue;
|
||||
} else if (err < 0) {
|
||||
uac_loge("Playback writei error: %s\n", snd_strerror(err));
|
||||
break;
|
||||
} else if (err != cptr) {
|
||||
uac_logi("Short write (expected %u, wrote %d)\n", cptr, err);
|
||||
break;
|
||||
}
|
||||
#else
|
||||
if (err < 0) {
|
||||
err = snd_pcm_recover(handle, err, 0);
|
||||
}
|
||||
|
||||
if (err < 0) {
|
||||
printf("snd_pcm_writei failed: %s\n", snd_strerror(err));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
cptr -= err;
|
||||
}
|
||||
uac_logd("Playback stream ok\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alsa_playback_stop(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alsa_capture_init(void)
|
||||
{
|
||||
// init c_args
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alsa_capture_deinit(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alsa_capture_open(void)
|
||||
{
|
||||
int err;
|
||||
int buf_size;
|
||||
snd_output_t *output = NULL;
|
||||
snd_pcm_hw_params_t *hwparams = NULL;
|
||||
|
||||
err = snd_output_stdio_attach(&output, stdout, 0);
|
||||
if (err < 0) {
|
||||
uac_logw("Output failed: %s\n", snd_strerror(err));
|
||||
}
|
||||
|
||||
err = snd_pcm_open(&g_handle_capture, device, SND_PCM_STREAM_CAPTURE, 0);
|
||||
if (err < 0) {
|
||||
uac_loge("snd_pcm_open capture failed: %s.\n", snd_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
snd_pcm_hw_params_alloca(&hwparams);
|
||||
|
||||
err = set_hwparams(g_handle_capture, hwparams, &c_args);
|
||||
if (err < 0) {
|
||||
goto hw_err;
|
||||
}
|
||||
|
||||
buf_size = (c_args.period_size * c_args.channels * snd_pcm_format_physical_width(c_args.format)) / 8;
|
||||
g_capture_data.addr = malloc(buf_size);
|
||||
if (g_capture_data.addr == NULL) {
|
||||
uac_loge("No enough memory\n");
|
||||
goto hw_err;
|
||||
}
|
||||
|
||||
g_capture_data.size = buf_size;
|
||||
|
||||
uac_stream_config_capture(c_args.channels,
|
||||
c_args.rate,
|
||||
c_args.period_time / 1000,
|
||||
AUDIO_STREAM_FORMAT_S16_LE,
|
||||
1);
|
||||
|
||||
/* show the PCM setup parameters */
|
||||
snd_pcm_dump(g_handle_capture, output);
|
||||
|
||||
err = snd_pcm_start(g_handle_capture);
|
||||
if (err < 0) {
|
||||
uac_loge("snd_pcm_start capture failed: %s.\n", snd_strerror(err));
|
||||
goto sw_err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
sw_err:
|
||||
if (g_capture_data.addr != NULL) {
|
||||
free(g_capture_data.addr);
|
||||
g_capture_data.addr = NULL;
|
||||
g_capture_data.size = 0;
|
||||
}
|
||||
|
||||
hw_err:
|
||||
snd_pcm_close(g_handle_capture);
|
||||
g_handle_capture = NULL;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int alsa_capture_close(void)
|
||||
{
|
||||
if (g_capture_data.addr != NULL) {
|
||||
free(g_capture_data.addr);
|
||||
g_capture_data.addr = NULL;
|
||||
}
|
||||
|
||||
if (g_handle_capture != NULL) {
|
||||
snd_pcm_close(g_handle_capture);
|
||||
g_handle_capture = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Recv sample frame from ALSA, and send to AO */
|
||||
static int alsa_capture_run(int timeout_ms)
|
||||
{
|
||||
int err = 0;
|
||||
char* buffer = g_capture_data.addr;
|
||||
int size = g_capture_data.size;
|
||||
unsigned int period_size = c_args.period_size;
|
||||
snd_pcm_t* handle = g_handle_capture;
|
||||
|
||||
uac_logd("Capture stream send start.\n");
|
||||
while (err != period_size && g_capture_run) {
|
||||
err = snd_pcm_wait(handle, timeout_ms);
|
||||
if (err == 0) {
|
||||
uac_logd("Capture pcm wait timeout(%dms).\n", timeout_ms);
|
||||
continue;
|
||||
}
|
||||
|
||||
err = snd_pcm_readi(handle, buffer, period_size);
|
||||
if (err == -EPIPE) {
|
||||
/* EPIPE means overrun */
|
||||
uac_logw("Capture overrun occurred: %s.\n", snd_strerror(err));
|
||||
snd_pcm_prepare(handle);
|
||||
snd_pcm_start(handle);
|
||||
continue;
|
||||
} else if (err < 0) {
|
||||
uac_logw("Capture read failed: %s.\n", snd_strerror(err));
|
||||
continue;
|
||||
} else if (err != (int)period_size) {
|
||||
uac_logw("Short read (expected %u, rrote %d).\n", period_size, err);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (uac_stream_send_to_ao(buffer, size, timeout_ms) < 0) {
|
||||
uac_loge("Send to ao failed.\n");
|
||||
} else {
|
||||
uac_logd("Send to ao ok.\n");
|
||||
}
|
||||
}
|
||||
|
||||
uac_logd("Capture stream send ok.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alsa_capture_stop(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
/**************************************************************************************/
|
||||
|
||||
static pthread_t pid_playback = -1;
|
||||
static pthread_t pid_capture = -1;
|
||||
|
||||
static void* playback_process(void* args)
|
||||
{
|
||||
uac_logt("UAC playbcak process start.\n");
|
||||
uac_stream_startup_playback();
|
||||
// Waiting for AI to cache data, avoiding the issue of also underrun.
|
||||
usleep(20 * 1000);
|
||||
while (g_playback_run) {
|
||||
//alsa_playback_run(p_args.period_time / 1000);
|
||||
alsa_playback_run(PCM_WAIT_TIME_MS);
|
||||
}
|
||||
|
||||
alsa_playback_stop();
|
||||
uac_stream_shutdown_playback();
|
||||
uac_logt("UAC playback process exit.\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void* capture_process(void* args)
|
||||
{
|
||||
uac_logt("UAC capture process start.\n");
|
||||
uac_stream_startup_capture();
|
||||
while (g_capture_run) {
|
||||
//alsa_capture_run(c_args.period_time / 1000);
|
||||
alsa_capture_run(PCM_WAIT_TIME_MS);
|
||||
}
|
||||
|
||||
alsa_capture_stop();
|
||||
uac_stream_shutdown_capture();
|
||||
uac_logt("UAC capture process exit.\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/****************************** UAC Interface *****************************************/
|
||||
static int __uac_dev_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = alsa_playback_init();
|
||||
if (err < 0) {
|
||||
uac_loge("Playback init failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = alsa_capture_init();
|
||||
if (err < 0) {
|
||||
uac_loge("Capture init failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __uac_dev_deinit(void)
|
||||
{
|
||||
alsa_playback_deinit();
|
||||
alsa_capture_deinit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __uac_dev_open(void)
|
||||
{
|
||||
alsa_playback_open();
|
||||
alsa_capture_open();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __uac_dev_close(void)
|
||||
{
|
||||
alsa_playback_close();
|
||||
alsa_capture_close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __uac_dev_run(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
uac_logt("UAC run entry.\n");
|
||||
g_playback_run = 1;
|
||||
err = pthread_create(&pid_playback, NULL, &playback_process, NULL);
|
||||
if (err != 0) {
|
||||
uac_loge("Create playback_process failed.\n");
|
||||
}
|
||||
|
||||
g_capture_run = 1;
|
||||
err = pthread_create(&pid_capture, NULL, &capture_process, NULL);
|
||||
if (err != 0) {
|
||||
uac_loge("Creat capture_process failed.\n");
|
||||
}
|
||||
uac_logt("UAC run ok.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __uac_dev_stop(void)
|
||||
{
|
||||
uac_logt("UAC stop entry.\n");
|
||||
g_playback_run = 0;
|
||||
g_capture_run = 0;
|
||||
pthread_join(pid_playback, NULL);
|
||||
pthread_join(pid_capture, NULL);
|
||||
uac_logt("UAC stop ok.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uac_dev_t uac_dev = {
|
||||
.init = &__uac_dev_init,
|
||||
.deinit = &__uac_dev_deinit,
|
||||
.open = &__uac_dev_open,
|
||||
.close = &__uac_dev_close,
|
||||
.run = &__uac_dev_run,
|
||||
.stop = &__uac_dev_stop,
|
||||
};
|
||||
|
||||
uac_dev_t* get_uac_dev(void)
|
||||
{
|
||||
return &uac_dev;
|
||||
}
|
||||
|
||||
#define check_func(func_ptr, name) do { \
|
||||
if (func_ptr == NULL) { \
|
||||
uac_loge("Invalid %s function.\n", name); \
|
||||
return -1; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static int uac_check_strem_func(audio_stream_dev_t* func, int is_playback)
|
||||
{
|
||||
check_func(func->config, "config");
|
||||
check_func(func->startup, "startup");
|
||||
check_func(func->shutdown, "shutdown");
|
||||
if (is_playback) {
|
||||
check_func(func->recv_from_stream, "recv stream");
|
||||
} else {
|
||||
check_func(func->send_to_stream, "send stream");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uac_stream_register(audio_stream_operate_t* hand)
|
||||
{
|
||||
if (hand == NULL) {
|
||||
uac_loge("Invalid stream operate function.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hand->playback_stream == NULL) {
|
||||
uac_loge("Invalid playback_stream.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hand->capture_stream == NULL) {
|
||||
uac_loge("Invalid capture_stream.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (uac_check_strem_func(hand->playback_stream, 1) < 0) {
|
||||
uac_loge("Invalid playback operate function.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (uac_check_strem_func(hand->capture_stream, 0) < 0) {
|
||||
uac_loge("Invalid capture operate function.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uac_stream = hand;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uac_stream_unregister(void)
|
||||
{
|
||||
uac_stream = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**************************************************************************************/
|
||||
|
||||
/****************************** main **************************************************/
|
||||
#if 0
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
uac_dev_t* uac = get_uac_dev();
|
||||
|
||||
if (uac) {
|
||||
uac_dev->init();
|
||||
uac_dev->open();
|
||||
uac_dev->run();
|
||||
uac_dev->stop();
|
||||
uac_dev->close();
|
||||
uac_dev->deinit();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
/**************************************************************************************/
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __UAC_HAL_H__
|
||||
#define __UAC_HAL_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef enum audio_stream_format {
|
||||
AUDIO_STREAM_FORMAT_UNKNOWN = -1,
|
||||
/** Signed 8 bit */
|
||||
AUDIO_STREAM_FORMAT_S8 = 0,
|
||||
/** Unsigned 8 bit */
|
||||
AUDIO_STREAM_FORMAT_U8,
|
||||
/** Signed 16 bit Little Endian */
|
||||
AUDIO_STREAM_FORMAT_S16_LE,
|
||||
/** Signed 16 bit Big Endian */
|
||||
AUDIO_STREAM_FORMAT_S16_BE,
|
||||
/** Unsigned 16 bit Little Endian */
|
||||
AUDIO_STREAM_FORMAT_U16_LE,
|
||||
/** Unsigned 16 bit Big Endian */
|
||||
AUDIO_STREAM_FORMAT_U16_BE,
|
||||
/** Signed 24 bit Little Endian using low three bytes in 32-bit word */
|
||||
AUDIO_STREAM_FORMAT_S24_LE,
|
||||
/** Signed 24 bit Big Endian using low three bytes in 32-bit word */
|
||||
AUDIO_STREAM_FORMAT_S24_BE,
|
||||
/** Unsigned 24 bit Little Endian using low three bytes in 32-bit word */
|
||||
AUDIO_STREAM_FORMAT_U24_LE,
|
||||
/** Unsigned 24 bit Big Endian using low three bytes in 32-bit word */
|
||||
AUDIO_STREAM_FORMAT_U24_BE,
|
||||
/** Signed 32 bit Little Endian */
|
||||
AUDIO_STREAM_FORMAT_S32_LE,
|
||||
/** Signed 32 bit Big Endian */
|
||||
AUDIO_STREAM_FORMAT_S32_BE,
|
||||
/** Unsigned 32 bit Little Endian */
|
||||
AUDIO_STREAM_FORMAT_U32_LE,
|
||||
/** Unsigned 32 bit Big Endian */
|
||||
AUDIO_STREAM_FORMAT_U32_BE,
|
||||
} audio_stream_format_t;
|
||||
|
||||
typedef struct {
|
||||
int (*config)(unsigned int channels, unsigned int rate, unsigned int period_time,
|
||||
audio_stream_format_t format, bool is_interleaved);
|
||||
int (*startup)(void);
|
||||
int (*shutdown)(void);
|
||||
int (*recv_from_stream)(char* buf, int size, int timeout_ms);
|
||||
int (*send_to_stream)(char* buf, int size, int timeout_ms);
|
||||
} audio_stream_dev_t;
|
||||
|
||||
typedef struct {
|
||||
/* Get data from stream dev(AI) and send to uac dev */
|
||||
audio_stream_dev_t* playback_stream;
|
||||
/* Get data from uac dev and send to stream dev(AO) */
|
||||
audio_stream_dev_t* capture_stream;
|
||||
} audio_stream_operate_t;
|
||||
|
||||
typedef struct uac_dev {
|
||||
int (*init)(void);
|
||||
int (*deinit)(void);
|
||||
int (*open)(void);
|
||||
int (*close)(void);
|
||||
int (*run)(void);
|
||||
int (*stop)(void);
|
||||
} uac_dev_t;
|
||||
|
||||
uac_dev_t* get_uac_dev(void);
|
||||
int uac_stream_register(audio_stream_operate_t* hand);
|
||||
int uac_stream_unregister(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "uac_hal.h"
|
||||
#include "audio_stream.h"
|
||||
#include "sample_audio.h"
|
||||
|
||||
#include "uac_log.h"
|
||||
|
||||
struct audio_format_map {
|
||||
audio_stream_format_t hal_format;
|
||||
audio_bit_depth bit_format;
|
||||
};
|
||||
|
||||
struct audio_format_map format_group[] = {
|
||||
{AUDIO_STREAM_FORMAT_S8, AUDIO_BIT_DEPTH_8},
|
||||
{AUDIO_STREAM_FORMAT_S16_LE, AUDIO_BIT_DEPTH_16},
|
||||
{AUDIO_STREAM_FORMAT_S24_LE, AUDIO_BIT_DEPTH_24},
|
||||
{AUDIO_STREAM_FORMAT_S32_LE, AUDIO_BIT_DEPTH_32},
|
||||
};
|
||||
|
||||
static int format_hal_to_bit(audio_stream_format_t format)
|
||||
{
|
||||
int i;
|
||||
int nums = sizeof(format_group) / sizeof(struct audio_format_map);
|
||||
|
||||
for (i = 0; i < nums; i++) {
|
||||
if (format_group[i].hal_format == format) {
|
||||
return format_group[i].bit_format;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int playback_stream_config(unsigned int channels,
|
||||
unsigned int rate,
|
||||
unsigned int period_time,
|
||||
audio_stream_format_t format,
|
||||
bool is_interleaved)
|
||||
{
|
||||
audio_bit_depth bit_format = format_hal_to_bit(format);
|
||||
|
||||
uac_logd("channels: %u\n", channels);
|
||||
uac_logd("rate: %u\n", rate);
|
||||
uac_logd("period_time: %ums\n", period_time);
|
||||
uac_logd("format: %d\n", bit_format);
|
||||
uac_logd("interleaved: %d\n", is_interleaved);
|
||||
|
||||
return sample_audio_ai_config(channels, rate, period_time, bit_format, is_interleaved);
|
||||
}
|
||||
|
||||
static int playback_stream_startup(void)
|
||||
{
|
||||
return sample_audio_ai_startup();
|
||||
}
|
||||
|
||||
static int playback_stream_shutdown(void)
|
||||
{
|
||||
return sample_audio_ai_shutdown();
|
||||
}
|
||||
|
||||
/* Get stream from AI */
|
||||
static int playback_recv_from_stream(char* buf, int size, int timeout_ms)
|
||||
{
|
||||
return sample_audio_get_frame_from_ai(buf, size, timeout_ms);
|
||||
}
|
||||
|
||||
static int capture_stream_config(unsigned int channels,
|
||||
unsigned int rate,
|
||||
unsigned int period_time,
|
||||
audio_stream_format_t format,
|
||||
bool is_interleaved)
|
||||
{
|
||||
audio_bit_depth bit_format = format_hal_to_bit(format);
|
||||
|
||||
uac_logd("channels: %u\n", channels);
|
||||
uac_logd("rate: %u\n", rate);
|
||||
uac_logd("period_time: %ums\n", period_time);
|
||||
uac_logd("format: %d\n", bit_format);
|
||||
uac_logd("interleaved: %d\n", is_interleaved);
|
||||
|
||||
return sample_audio_ao_config(channels, rate, period_time, bit_format, is_interleaved);
|
||||
}
|
||||
|
||||
static int capture_stream_startup(void)
|
||||
{
|
||||
return sample_audio_ao_startup();
|
||||
}
|
||||
|
||||
static int capture_stream_shutdown(void)
|
||||
{
|
||||
return sample_audio_ao_shutdown();
|
||||
}
|
||||
|
||||
/* Send stream to AO */
|
||||
static int capture_send_to_stream(char* buf, int size, int timeout_ms)
|
||||
{
|
||||
return sample_audio_send_frame_to_ao(buf, size, timeout_ms);
|
||||
}
|
||||
|
||||
static audio_stream_dev_t playback_stream_dev = {
|
||||
.config = playback_stream_config,
|
||||
.startup = playback_stream_startup,
|
||||
.shutdown = playback_stream_shutdown,
|
||||
.recv_from_stream = playback_recv_from_stream,
|
||||
};
|
||||
|
||||
static audio_stream_dev_t capture_stream_dev = {
|
||||
.config = capture_stream_config,
|
||||
.startup = capture_stream_startup,
|
||||
.shutdown = capture_stream_shutdown,
|
||||
.send_to_stream = capture_send_to_stream,
|
||||
};
|
||||
|
||||
static audio_stream_operate_t strem_operate = {
|
||||
.playback_stream = &playback_stream_dev,
|
||||
.capture_stream = &capture_stream_dev,
|
||||
};
|
||||
|
||||
audio_stream_operate_t* get_audio_stream(void)
|
||||
{
|
||||
return &strem_operate;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __AUDIO_STREAM_H__
|
||||
#define __AUDIO_STREAM_H__
|
||||
|
||||
#include "uac_hal.h"
|
||||
|
||||
audio_stream_operate_t* get_audio_stream(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,569 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "xmedia_audio_common.h"
|
||||
#include "xmedia_audio_ai.h"
|
||||
//#include "xmedia_audio_vqe_common.h"
|
||||
#include "xmedia_audio_vqe.h"
|
||||
#include "xmedia_audio_vqe_enhance_v1.h"
|
||||
|
||||
#include "sample_comm_audio.h"
|
||||
|
||||
#include "sample_audio.h"
|
||||
#include "uac_log.h"
|
||||
|
||||
#define SAMPLE_AUDIO_DBG printf
|
||||
#define FRAME_TIME 10 //ms
|
||||
|
||||
// XMEDIA_AI_DEV_ADC0
|
||||
// XMEDIA_AI_DEV_I2S0
|
||||
// XMEDIA_AI_DEV_PDM0
|
||||
// XMEDIA_AI_DEV_PDM_I2S0
|
||||
#define AI_DEV XMEDIA_AI_DEV_ADC0
|
||||
#define AI_CHANNELS 1
|
||||
|
||||
// XMEDIA_AO_DEV_DAC0
|
||||
// XMEDIA_AO_DEV_I2S0
|
||||
#define AO_DEV XMEDIA_AO_DEV_DAC0
|
||||
#define AO_CHANNELS 1
|
||||
|
||||
static pthread_mutex_t mutex_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
#define sample_audio_lock() pthread_mutex_lock(&mutex_lock)
|
||||
#define sample_audio_unlock() pthread_mutex_unlock(&mutex_lock)
|
||||
|
||||
#define MIN(val1, val2) ((val1) > (val2) ? (val2) : (val1))
|
||||
|
||||
struct user_args {
|
||||
unsigned int channels;
|
||||
unsigned int sample_rate;
|
||||
unsigned int period_time; /* ms */
|
||||
unsigned int format; /* audio_bit_depth */
|
||||
bool is_interleaved;
|
||||
};
|
||||
|
||||
static unsigned int g_audio_init = 0;
|
||||
|
||||
static sample_ai_config g_ai_config = {
|
||||
.dev = AI_DEV,
|
||||
};
|
||||
static ai_vqe_attr_v1 ai_vqev1_attr = {0};
|
||||
static struct user_args g_ai_args = {
|
||||
.channels = AI_CHANNELS,
|
||||
.sample_rate = AUDIO_SAMPLE_RATE_8000,
|
||||
.period_time = FRAME_TIME,
|
||||
.format = AUDIO_BIT_DEPTH_16,
|
||||
.is_interleaved = 1,
|
||||
};
|
||||
|
||||
static sample_ao_config g_ao_config = {
|
||||
.dev = AO_DEV,
|
||||
};
|
||||
static ao_vqe_attr_v1 ao_vqev1_attr = {0};
|
||||
static struct user_args g_ao_args = {
|
||||
.channels = AO_CHANNELS,
|
||||
.sample_rate = AUDIO_SAMPLE_RATE_8000,
|
||||
.period_time = FRAME_TIME,
|
||||
.format = AUDIO_BIT_DEPTH_16,
|
||||
.is_interleaved = 1,
|
||||
};
|
||||
|
||||
static xmedia_void load_ai_vqev1_attr(ai_vqe_attr* attr)
|
||||
{
|
||||
ai_vqe_attr_v1 *vqev1_attr = &ai_vqev1_attr;
|
||||
|
||||
attr->version = AI_VQE_ENHANCE_ATTR_VERSION_1;
|
||||
attr->work_sample_rate = (g_ai_args.sample_rate == AUDIO_SAMPLE_RATE_8000) ?
|
||||
AUDIO_SAMPLE_RATE_8000 : AUDIO_SAMPLE_RATE_16000;
|
||||
attr->in_channels = g_ai_args.channels;
|
||||
attr->attr = (xmedia_void*)&ai_vqev1_attr;
|
||||
|
||||
vqev1_attr->mask = VQE_V1_MASK_AGC | VQE_V1_MASK_ANR | VQE_V1_MASK_HPF;
|
||||
|
||||
vqev1_attr->agc_attr.mode = VQE_V1_USR_MODE_AUTO;
|
||||
vqev1_attr->agc_attr.target_level = -6;
|
||||
vqev1_attr->agc_attr.max_boost_gain = 20;
|
||||
vqev1_attr->agc_attr.noise_floor = -100;
|
||||
vqev1_attr->agc_attr.ratio = 12;
|
||||
vqev1_attr->agc_attr.attack_time = 8;
|
||||
vqev1_attr->agc_attr.release_time =20;
|
||||
|
||||
vqev1_attr->anr_attr.mode = VQE_V1_USR_MODE_AUTO;
|
||||
vqev1_attr->anr_attr.usr_scene = VQE_V1_ANR_SCENE_NORMAL;
|
||||
vqev1_attr->anr_attr.nr_mode = VQE_V1_NR_MODE_SPEECH;
|
||||
vqev1_attr->anr_attr.max_suppress_gain = 6;
|
||||
vqev1_attr->anr_attr.suppress_level = 0x2;
|
||||
vqev1_attr->anr_attr.nonstationary_suppress_level = 0x0;
|
||||
|
||||
vqev1_attr->vad_attr.mode = VQE_V1_USR_MODE_AUTO;
|
||||
vqev1_attr->vad_attr.sensitivity_level = 4;
|
||||
|
||||
vqev1_attr->hpf_attr.mode = VQE_V1_USR_MODE_AUTO;
|
||||
vqev1_attr->hpf_attr.freq = VQE_V1_HPF_FREQ_80;
|
||||
|
||||
memset(&vqev1_attr->eq_attr, 0, sizeof(vqe_eq_attr_v1));
|
||||
|
||||
vqev1_attr->eq_attr.gain[0] = 0;
|
||||
vqev1_attr->eq_attr.gain[1] = 0;
|
||||
vqev1_attr->eq_attr.gain[2] = 0;
|
||||
vqev1_attr->eq_attr.gain[3] = 0;
|
||||
vqev1_attr->eq_attr.gain[4] = 0;
|
||||
vqev1_attr->eq_attr.gain[5] = 0;
|
||||
vqev1_attr->eq_attr.gain[6] = 0;
|
||||
vqev1_attr->eq_attr.gain[7] = 0;
|
||||
vqev1_attr->eq_attr.gain[8] = 0;
|
||||
vqev1_attr->eq_attr.gain[9] = 0;
|
||||
vqev1_attr->eq_attr.gain[10] = 0;
|
||||
vqev1_attr->eq_attr.gain[11] = 0;
|
||||
vqev1_attr->eq_attr.gain[12] = 0;
|
||||
vqev1_attr->eq_attr.gain[13] = 0;
|
||||
vqev1_attr->eq_attr.gain[14] = 0;
|
||||
vqev1_attr->eq_attr.gain[15] = 0;
|
||||
vqev1_attr->eq_attr.gain[16] = 0;
|
||||
vqev1_attr->eq_attr.gain[17] = 0;
|
||||
vqev1_attr->eq_attr.gain[18] = 0;
|
||||
vqev1_attr->eq_attr.gain[19] = 0;
|
||||
}
|
||||
|
||||
static xmedia_s32 sample_ai_config_init(xmedia_void)
|
||||
{
|
||||
g_ai_config.dev = AI_DEV;
|
||||
|
||||
sample_comm_ai_get_default_attr(g_ai_config.dev, &g_ai_config.dev_attr);
|
||||
g_ai_config.dev_attr.mode = AUDIO_DEV_WORK_MODE_MONO;
|
||||
g_ai_config.dev_attr.channels = g_ai_args.channels;
|
||||
g_ai_config.dev_attr.bit_depth = g_ai_args.format;
|
||||
g_ai_config.dev_attr.sample_rate = g_ai_args.sample_rate;
|
||||
//g_ai_config.dev_attr.pcm_frame_num = ;
|
||||
g_ai_config.dev_attr.pcm_samples_per_frame = g_ai_args.period_time * g_ai_args.sample_rate / 1000;
|
||||
|
||||
g_ai_config.chn_attr.sample_rate = g_ai_args.sample_rate;
|
||||
g_ai_config.chn_attr.interleaved = g_ai_args.is_interleaved;
|
||||
|
||||
g_ai_config.res_enable = XMEDIA_FALSE;
|
||||
if ((g_ai_config.dev_attr.sample_rate != AUDIO_SAMPLE_RATE_8000 &&
|
||||
g_ai_config.dev_attr.sample_rate != AUDIO_SAMPLE_RATE_16000) ||
|
||||
(g_ai_config.chn_attr.sample_rate != g_ai_config.dev_attr.sample_rate)) {
|
||||
g_ai_config.res_enable = XMEDIA_TRUE;
|
||||
}
|
||||
|
||||
g_ai_config.vqe_enable = XMEDIA_TRUE;
|
||||
load_ai_vqev1_attr(&g_ai_config.vqe_attr);
|
||||
|
||||
g_ai_config.source = AI_EC_SOURCE_AO_DAC0;
|
||||
g_ai_config.ec_chn_id = 0;
|
||||
|
||||
return XMEDIA_SUCCESS;
|
||||
}
|
||||
|
||||
static xmedia_s32 sample_start_ai()
|
||||
{
|
||||
xmedia_s32 ret, vol;
|
||||
|
||||
ret = sample_ai_config_init();
|
||||
CHECK_RET(ret, "sample_ai_config_init");
|
||||
|
||||
ret = sample_comm_ai_register(ai_vqev1_attr.mask, g_ai_config.vqe_attr.version, g_ai_config.res_enable);
|
||||
CHECK_RET(ret, "sample_comm_ai_register");
|
||||
|
||||
ret = sample_comm_ai_start(&g_ai_config);
|
||||
CHECK_RET(ret, "sample_comm_ai_start");
|
||||
|
||||
vol = 10;
|
||||
ret = sample_comm_ai_set_volume(&g_ai_config, vol);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
uac_logw("Set ai volume %d return 0x%x.\n", vol, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static xmedia_s32 sample_stop_ai(xmedia_void)
|
||||
{
|
||||
xmedia_s32 ret;
|
||||
|
||||
ret = sample_comm_ai_stop(&g_ai_config);
|
||||
CHECK_RET(ret, "sample_comm_ai_stop");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static xmedia_s32 sample_recv_frame_from_ai(char* buf, int size, int time_out)
|
||||
{
|
||||
xmedia_s32 ret;
|
||||
audio_frame frame;
|
||||
audio_ext_frame ext_frame;
|
||||
xmedia_u32 copy_size;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < g_ai_config.dev_attr.channels; i++) {
|
||||
ret = xmedia_ai_acquire_frame(g_ai_config.dev, i, &frame, &ext_frame, time_out);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
uac_loge("Acquire audio frame from AI failed, error=%d.\n", ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
copy_size = MIN(size, frame.size);
|
||||
|
||||
if (size != frame.size) {
|
||||
uac_logw("frame.size(%d) != buf.size(%d).\n", frame.size, size);
|
||||
}
|
||||
|
||||
memcpy(buf, frame.data, copy_size);
|
||||
|
||||
xmedia_ai_release_frame(g_ai_config.dev, i, &frame, &ext_frame);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static xmedia_void load_ao_vqev1_attr(ao_vqe_attr* attr)
|
||||
{
|
||||
ao_vqe_attr_v1 *vqev1_attr = &ao_vqev1_attr;
|
||||
|
||||
attr->version = AO_VQE_ENHANCE_ATTR_VERSION_1;
|
||||
attr->attr = (xmedia_void*)&ao_vqev1_attr;
|
||||
|
||||
vqev1_attr->mask = VQE_V1_MASK_AGC | VQE_V1_MASK_ANR | VQE_V1_MASK_HPF;
|
||||
|
||||
vqev1_attr->agc_attr.mode = VQE_V1_USR_MODE_AUTO;
|
||||
vqev1_attr->agc_attr.target_level = -6;
|
||||
vqev1_attr->agc_attr.max_boost_gain = 20;
|
||||
vqev1_attr->agc_attr.noise_floor = -65;
|
||||
vqev1_attr->agc_attr.ratio = 10;
|
||||
vqev1_attr->agc_attr.attack_time = 20;
|
||||
vqev1_attr->agc_attr.release_time =20;
|
||||
|
||||
vqev1_attr->anr_attr.mode = VQE_V1_USR_MODE_AUTO;
|
||||
vqev1_attr->anr_attr.usr_scene = VQE_V1_ANR_SCENE_NORMAL;
|
||||
vqev1_attr->anr_attr.nr_mode = VQE_V1_NR_MODE_SPEECH;
|
||||
vqev1_attr->anr_attr.max_suppress_gain = 6;
|
||||
vqev1_attr->anr_attr.suppress_level = 0x2;
|
||||
vqev1_attr->anr_attr.nonstationary_suppress_level = 0x2;
|
||||
|
||||
vqev1_attr->hpf_attr.mode = VQE_V1_USR_MODE_AUTO;
|
||||
vqev1_attr->hpf_attr.freq = VQE_V1_HPF_FREQ_80;
|
||||
|
||||
memset(&vqev1_attr->eq_attr, 0, sizeof(vqe_eq_attr_v1));
|
||||
|
||||
vqev1_attr->eq_attr.gain[0] = 0;
|
||||
vqev1_attr->eq_attr.gain[1] = 0;
|
||||
vqev1_attr->eq_attr.gain[2] = 0;
|
||||
vqev1_attr->eq_attr.gain[3] = 0;
|
||||
vqev1_attr->eq_attr.gain[4] = 0;
|
||||
vqev1_attr->eq_attr.gain[5] = 0;
|
||||
vqev1_attr->eq_attr.gain[6] = 0;
|
||||
vqev1_attr->eq_attr.gain[7] = 0;
|
||||
vqev1_attr->eq_attr.gain[8] = 0;
|
||||
vqev1_attr->eq_attr.gain[9] = 0;
|
||||
vqev1_attr->eq_attr.gain[10] = 0;
|
||||
vqev1_attr->eq_attr.gain[11] = 0;
|
||||
vqev1_attr->eq_attr.gain[12] = 0;
|
||||
vqev1_attr->eq_attr.gain[13] = 0;
|
||||
vqev1_attr->eq_attr.gain[14] = 0;
|
||||
vqev1_attr->eq_attr.gain[15] = 0;
|
||||
vqev1_attr->eq_attr.gain[16] = 0;
|
||||
vqev1_attr->eq_attr.gain[17] = 0;
|
||||
vqev1_attr->eq_attr.gain[18] = 0;
|
||||
vqev1_attr->eq_attr.gain[19] = 0;
|
||||
}
|
||||
|
||||
static xmedia_s32 sample_ao_config_init(xmedia_void)
|
||||
{
|
||||
g_ao_config.dev = AO_DEV;
|
||||
|
||||
sample_comm_ao_get_default_attr(g_ao_config.dev, &g_ao_config.dev_attr);
|
||||
g_ao_config.dev_attr.mode = AUDIO_DEV_WORK_MODE_MONO;
|
||||
g_ao_config.dev_attr.channels = g_ao_args.channels;
|
||||
g_ao_config.dev_attr.bit_depth = g_ao_args.format;
|
||||
g_ao_config.dev_attr.sample_rate = g_ao_args.sample_rate;
|
||||
//g_ao_config.dev_attr.pcm_frame_max_num = ;
|
||||
g_ao_config.dev_attr.pcm_samples_per_frame = g_ao_args.period_time * g_ao_args.sample_rate / 1000;
|
||||
|
||||
g_ao_config.res_enable = XMEDIA_FALSE;
|
||||
if ((g_ao_config.dev_attr.sample_rate != AUDIO_SAMPLE_RATE_8000 &&
|
||||
g_ao_config.dev_attr.sample_rate != AUDIO_SAMPLE_RATE_16000) ||
|
||||
(g_ao_config.frame_info.frm_rate != g_ao_config.dev_attr.sample_rate)) {
|
||||
g_ao_config.res_enable = XMEDIA_TRUE;
|
||||
}
|
||||
|
||||
g_ao_config.vqe_enable = XMEDIA_TRUE;
|
||||
load_ao_vqev1_attr(&g_ao_config.vqe_attr);
|
||||
//g_ao_config.binder_src = ;
|
||||
|
||||
g_ao_config.frame_info.frm_rate = g_ao_args.sample_rate;
|
||||
g_ao_config.frame_info.frm_chn = g_ao_args.channels;
|
||||
g_ao_config.frame_info.frm_bit = g_ao_args.format;
|
||||
|
||||
return XMEDIA_SUCCESS;
|
||||
}
|
||||
|
||||
static xmedia_s32 sample_start_ao()
|
||||
{
|
||||
xmedia_s32 ret, vol;
|
||||
|
||||
ret = sample_ao_config_init();
|
||||
CHECK_RET(ret, "sample_ao_config_init");
|
||||
|
||||
ret = sample_comm_ao_register(ao_vqev1_attr.mask, g_ao_config.vqe_attr.version, g_ao_config.res_enable);
|
||||
CHECK_RET(ret, "sample_comm_ao_register");
|
||||
|
||||
ret = sample_comm_ao_start(&g_ao_config);
|
||||
CHECK_RET(ret, "sample_comm_ao_start");
|
||||
|
||||
vol = 10;
|
||||
ret = sample_comm_ao_set_volume(&g_ao_config, vol);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
uac_logw("Set ao volume %d return 0x%x.\n", vol, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static xmedia_s32 sample_stop_ao(xmedia_void)
|
||||
{
|
||||
xmedia_s32 ret;
|
||||
|
||||
ret = sample_comm_ao_stop(&g_ao_config);
|
||||
CHECK_RET(ret, "sample_comm_ao_stop");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int get_sample_bytes(void)
|
||||
{
|
||||
switch (g_ao_args.format) {
|
||||
case AUDIO_BIT_DEPTH_8:
|
||||
return 1;
|
||||
|
||||
case AUDIO_BIT_DEPTH_16:
|
||||
return 2;
|
||||
|
||||
case AUDIO_BIT_DEPTH_24:
|
||||
return 3;
|
||||
|
||||
case AUDIO_BIT_DEPTH_32:
|
||||
return 4;
|
||||
|
||||
case AUDIO_BIT_DEPTH_UNKNOWN:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static xmedia_s32 sample_send_frame_to_ao(char* buf, int size, int time_out)
|
||||
{
|
||||
int ret, i;
|
||||
audio_frame frame;
|
||||
xmedia_u32 send_size;
|
||||
unsigned int period_time = g_ao_args.period_time;
|
||||
unsigned int sample_bytes = get_sample_bytes();
|
||||
|
||||
frame.bit_depth = g_ao_config.frame_info.frm_bit;
|
||||
frame.channels = g_ao_config.frame_info.frm_chn;
|
||||
frame.interleaved = g_ao_args.is_interleaved;
|
||||
frame.sample_rate = g_ao_config.frame_info.frm_rate;
|
||||
frame.timestamp = -1;
|
||||
|
||||
// frame.sample_rate / 1000 : Number of samples per millisecond
|
||||
// period_time : Milliseconds of one cycle
|
||||
// frame.channels * sample_bytes : The byte size of a sample.
|
||||
send_size = (frame.sample_rate / 1000) * (frame.channels * sample_bytes) * period_time;
|
||||
|
||||
frame.data = (xmedia_void*)buf;
|
||||
frame.size = MIN(send_size, size);
|
||||
|
||||
if (send_size != size) {
|
||||
uac_logw("send_size(%d) != buf.size(%d).\n", send_size, size);
|
||||
}
|
||||
|
||||
for (i = 0; i < g_ao_config.dev_attr.channels; i++) {
|
||||
ret = xmedia_ao_send_frame(g_ao_config.dev, i, &frame, time_out);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
uac_loge("xmedia_ao_send_frame failed, error=%d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static xmedia_s32 comm_audio_init()
|
||||
{
|
||||
xmedia_s32 ret;
|
||||
|
||||
ret = xmedia_ai_init();
|
||||
CHECK_RET(ret, "xmedia_ai_init");
|
||||
|
||||
ret = xmedia_ao_init();
|
||||
CHECK_RET(ret, "xmedia_ao_init");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static xmedia_s32 comm_audio_exit()
|
||||
{
|
||||
xmedia_s32 ret;
|
||||
|
||||
ret = xmedia_ai_exit();
|
||||
CHECK_RET(ret, "xmedia_ai_exit");
|
||||
|
||||
ret = xmedia_ao_exit();
|
||||
CHECK_RET(ret, "xmedia_ao_exit");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static xmedia_s32 sample_init_audio(void)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
sample_audio_lock();
|
||||
if (g_audio_init) {
|
||||
g_audio_init++;
|
||||
sample_audio_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = comm_audio_init();
|
||||
if (err < 0) {
|
||||
uac_loge("sample_comm_audio_init failed.\n");
|
||||
sample_audio_unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
g_audio_init++;
|
||||
sample_audio_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static xmedia_s32 sample_deinit_audio(void)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
sample_audio_lock();
|
||||
if (!g_audio_init) {
|
||||
sample_audio_unlock();
|
||||
return 0;
|
||||
} else {
|
||||
g_audio_init--;
|
||||
}
|
||||
|
||||
if (g_audio_init == 0) {
|
||||
err = comm_audio_exit();
|
||||
if (err < 0) {
|
||||
uac_loge("comm_audio_exit failed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
sample_audio_unlock();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int sample_audio_ai_config(unsigned int channels,
|
||||
unsigned int rate,
|
||||
unsigned int period_time,
|
||||
unsigned int format,
|
||||
bool is_interleaved)
|
||||
{
|
||||
g_ai_args.channels = channels;
|
||||
g_ai_args.sample_rate = rate;
|
||||
g_ai_args.period_time = period_time;
|
||||
g_ai_args.format = format;
|
||||
g_ai_args.is_interleaved = is_interleaved;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_audio_ai_startup(void)
|
||||
{
|
||||
if (sample_init_audio() < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sample_start_ai() < 0) {
|
||||
sample_deinit_audio();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_audio_ai_shutdown(void)
|
||||
{
|
||||
sample_stop_ai();
|
||||
|
||||
sample_deinit_audio();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_audio_get_frame_from_ai(char* buf, int size, int time_out)
|
||||
{
|
||||
if (buf == NULL || size <= 0) {
|
||||
uac_loge("Invalid param.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sample_recv_frame_from_ai(buf, size, time_out);
|
||||
}
|
||||
|
||||
int sample_audio_ao_config(unsigned int channels,
|
||||
unsigned int rate,
|
||||
unsigned int period_time,
|
||||
unsigned int format,
|
||||
bool is_interleaved)
|
||||
{
|
||||
g_ao_args.channels = channels;
|
||||
g_ao_args.sample_rate = rate;
|
||||
g_ao_args.period_time = period_time;
|
||||
g_ao_args.format = format;
|
||||
g_ao_args.is_interleaved = is_interleaved;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_audio_ao_startup(void)
|
||||
{
|
||||
if (sample_init_audio() < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sample_start_ao() < 0) {
|
||||
sample_deinit_audio();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_audio_ao_shutdown(void)
|
||||
{
|
||||
sample_stop_ao();
|
||||
|
||||
sample_deinit_audio();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_audio_send_frame_to_ao(char* buf, int size, int time_out)
|
||||
{
|
||||
if (buf == NULL || size <= 0) {
|
||||
uac_loge("Invlaid params.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sample_send_frame_to_ao(buf, size, time_out);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __SAMPLE_AUDIO_H__
|
||||
#define __SAMPLE_AUDIO_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "xmedia_audio_common.h"
|
||||
|
||||
int sample_audio_ai_config(unsigned int channels,
|
||||
unsigned int rate,
|
||||
unsigned int period_time,
|
||||
audio_bit_depth format,
|
||||
bool is_interleaved);
|
||||
int sample_audio_ai_startup(void);
|
||||
int sample_audio_ai_shutdown(void);
|
||||
int sample_audio_get_frame_from_ai(char* buf, int size, int time_out);
|
||||
|
||||
int sample_audio_ao_config(unsigned int channels,
|
||||
unsigned int rate,
|
||||
unsigned int period_time,
|
||||
audio_bit_depth format,
|
||||
bool is_interleaved);
|
||||
int sample_audio_ao_startup(void);
|
||||
int sample_audio_ao_shutdown(void);
|
||||
int sample_audio_send_frame_to_ao(char* buf, int size, int time_out);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
|
||||
#include "uac.h"
|
||||
#include "uac_hal.h"
|
||||
#include "audio_stream.h"
|
||||
|
||||
#include "uac_log.h"
|
||||
|
||||
static uac_dev_t* uac_dev = NULL;
|
||||
|
||||
static int __uac_init(void)
|
||||
{
|
||||
int err = 0;
|
||||
audio_stream_operate_t* audio_stream = NULL;
|
||||
|
||||
audio_stream = get_audio_stream();
|
||||
if (audio_stream == NULL) {
|
||||
uac_loge("Create audio stream dev failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uac_dev = get_uac_dev();
|
||||
if (uac_dev == NULL) {
|
||||
uac_loge("Careate uac dev failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = uac_stream_register(audio_stream);
|
||||
if (err < 0) {
|
||||
uac_loge("UAC register stream dev failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (uac_dev->init) {
|
||||
err = uac_dev->init();
|
||||
} else {
|
||||
err = -1;
|
||||
uac_loge("Invalid uac_dev.\n");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __uac_deinit(void)
|
||||
{
|
||||
if (uac_dev && uac_dev->deinit) {
|
||||
uac_dev->deinit();
|
||||
uac_stream_unregister();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int __uac_open(void)
|
||||
{
|
||||
if (uac_dev && uac_dev->open) {
|
||||
return uac_dev->open();
|
||||
} else {
|
||||
uac_loge("Invalid uac_dev.\n");
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int __uac_close(void)
|
||||
{
|
||||
if (uac_dev && uac_dev->close) {
|
||||
return uac_dev->close();
|
||||
} else {
|
||||
uac_loge("Invalid uac_dev.\n");
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int __uac_run(void)
|
||||
{
|
||||
if (uac_dev && uac_dev->run) {
|
||||
return uac_dev->run();
|
||||
} else {
|
||||
uac_loge("Invalid uac_dev.\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __uac_stop(void)
|
||||
{
|
||||
if (uac_dev && uac_dev->stop) {
|
||||
return uac_dev->stop();
|
||||
} else {
|
||||
uac_loge("Invalid uac_dev.\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static uac_t __uac = {
|
||||
.init = &__uac_init,
|
||||
.deinit = &__uac_deinit,
|
||||
.open = &__uac_open,
|
||||
.close = &__uac_close,
|
||||
.run = &__uac_run,
|
||||
.stop = &__uac_stop,
|
||||
};
|
||||
|
||||
uac_t *get_uac(void)
|
||||
{
|
||||
return &__uac;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
#ifndef __UAC_LOG_H__
|
||||
#define __UAC_LOG_H__
|
||||
|
||||
#include "log_base.h"
|
||||
|
||||
#undef TAG
|
||||
#define TAG "UAC"
|
||||
|
||||
#define uac_loge LOGE
|
||||
#define uac_logw LOGW
|
||||
#define uac_logi LOGI
|
||||
#define uac_logt LOGT
|
||||
#define uac_logd LOGD
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "camera.h"
|
||||
#include "uvc.h"
|
||||
#include "uac.h"
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
extern unsigned int g_uvc;
|
||||
extern unsigned int g_uac;
|
||||
|
||||
static uvc_t* g_uvc_dev = NULL;
|
||||
static uac_t* g_uac_dev = NULL;
|
||||
|
||||
static int __camera_init(void)
|
||||
{
|
||||
if (g_uvc) {
|
||||
g_uvc_dev = get_uvc();
|
||||
if (g_uvc_dev && g_uvc_dev->init) {
|
||||
g_uvc_dev->init();
|
||||
}
|
||||
}
|
||||
|
||||
if (g_uac) {
|
||||
g_uac_dev = get_uac();
|
||||
if (g_uac_dev && g_uac_dev->init) {
|
||||
g_uac_dev->init();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __camera_deinit(void)
|
||||
{
|
||||
if (g_uac) {
|
||||
if (g_uac_dev && g_uac_dev->deinit) {
|
||||
g_uac_dev->deinit();
|
||||
}
|
||||
}
|
||||
|
||||
if (g_uvc) {
|
||||
if (g_uvc_dev && g_uvc_dev->deinit) {
|
||||
g_uvc_dev->deinit();
|
||||
}
|
||||
}
|
||||
|
||||
g_uvc_dev = NULL;
|
||||
g_uac_dev = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __camera_open(void)
|
||||
{
|
||||
if (g_uvc) {
|
||||
if (g_uvc_dev && g_uvc_dev->open) {
|
||||
g_uvc_dev->open();
|
||||
}
|
||||
}
|
||||
|
||||
if (g_uac) {
|
||||
if (g_uac_dev && g_uac_dev->open) {
|
||||
g_uac_dev->open();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __camera_close(void)
|
||||
{
|
||||
if (g_uac) {
|
||||
if (g_uac_dev && g_uac_dev->close) {
|
||||
g_uac_dev->close();
|
||||
}
|
||||
}
|
||||
|
||||
if (g_uvc) {
|
||||
if (g_uvc_dev && g_uvc_dev->close) {
|
||||
g_uvc_dev->close();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __camera_run(void)
|
||||
{
|
||||
if (g_uvc) {
|
||||
if (g_uvc_dev && g_uvc_dev->run) {
|
||||
g_uvc_dev->run();
|
||||
}
|
||||
}
|
||||
|
||||
if (g_uac) {
|
||||
if (g_uac_dev && g_uac_dev->run) {
|
||||
g_uac_dev->run();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __camera_stop(void)
|
||||
{
|
||||
if (g_uvc) {
|
||||
if (g_uvc_dev && g_uvc_dev->stop) {
|
||||
g_uvc_dev->stop();
|
||||
}
|
||||
}
|
||||
|
||||
if (g_uac) {
|
||||
if (g_uac_dev && g_uac_dev->stop) {
|
||||
g_uac_dev->stop();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static camera __camera =
|
||||
{
|
||||
.init = &__camera_init,
|
||||
.deinit = &__camera_deinit,
|
||||
.open = &__camera_open,
|
||||
.close = &__camera_close,
|
||||
.run = &__camera_run,
|
||||
.stop = &__camera_stop,
|
||||
};
|
||||
|
||||
camera *get_camera(void)
|
||||
{
|
||||
return &__camera;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __CAMERA_H__
|
||||
#define __CAMERA_H__
|
||||
|
||||
typedef struct camera {
|
||||
int (*init)(void);
|
||||
int (*deinit)(void);
|
||||
int (*open)(void);
|
||||
int (*close)(void);
|
||||
int (*run)(void);
|
||||
int (*stop)(void);
|
||||
} camera;
|
||||
|
||||
camera *get_camera(void);
|
||||
|
||||
#endif //__CAMERA_H__
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config_svc.h"
|
||||
#include "iniparser.h"
|
||||
#include "log_base.h"
|
||||
|
||||
#define CONFIG_MAX_PATH 256
|
||||
|
||||
typedef struct {
|
||||
char* config_file;
|
||||
} config_svc_t;
|
||||
|
||||
static config_svc_t* __config_svc = NULL;
|
||||
|
||||
int create_config_svc(char *config_path)
|
||||
{
|
||||
assert(config_path != NULL);
|
||||
|
||||
if (__config_svc != NULL) {
|
||||
free(__config_svc);
|
||||
}
|
||||
|
||||
__config_svc = (config_svc_t*)malloc(sizeof(config_svc_t));
|
||||
if (__config_svc == NULL) {
|
||||
LOGE("create config svc failure\n");
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
__config_svc->config_file = malloc(sizeof(char) * CONFIG_MAX_PATH);
|
||||
if (__config_svc->config_file == NULL) {
|
||||
LOGE("malloc config file memory failure\n");
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
if (strlen(config_path) > CONFIG_MAX_PATH) {
|
||||
LOGE("config path is too length\n");
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
strcpy(__config_svc->config_file, config_path);
|
||||
|
||||
return 0;
|
||||
|
||||
ERR:
|
||||
if (__config_svc) {
|
||||
if (__config_svc->config_file) {
|
||||
free(__config_svc->config_file);
|
||||
}
|
||||
|
||||
free(__config_svc);
|
||||
}
|
||||
|
||||
__config_svc = NULL;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void release_cofnig_svc()
|
||||
{
|
||||
if (__config_svc) {
|
||||
if (__config_svc->config_file) {
|
||||
free(__config_svc->config_file);
|
||||
}
|
||||
|
||||
free(__config_svc);
|
||||
}
|
||||
|
||||
__config_svc = NULL;
|
||||
}
|
||||
|
||||
int get_config_value(const char *key, int default_value)
|
||||
{
|
||||
dictionary *config_dic;
|
||||
int value = default_value;
|
||||
|
||||
assert(key != NULL);
|
||||
assert(__config_svc != NULL);
|
||||
|
||||
config_dic = iniparser_load(__config_svc->config_file);
|
||||
|
||||
if (config_dic == NULL) {
|
||||
LOGE("open config file failuer[%s], returen default value:%d\n", __config_svc->config_file, default_value);
|
||||
return value;
|
||||
}
|
||||
|
||||
value = iniparser_getint(config_dic, key, default_value);
|
||||
iniparser_freedict(config_dic);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
int get_config_string(const char *key, char *buf, int size)
|
||||
{
|
||||
dictionary *config_dic;
|
||||
int copy_size;
|
||||
int ret = -1;
|
||||
char* value = NULL;
|
||||
|
||||
assert(key != NULL);
|
||||
assert(buf != NULL);
|
||||
assert(__config_svc != NULL);
|
||||
|
||||
config_dic = iniparser_load(__config_svc->config_file);
|
||||
if (config_dic == NULL) {
|
||||
LOGE("open config file failuer[%s], returen NULL\n", __config_svc->config_file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
value = iniparser_getstring(config_dic, key, NULL);
|
||||
if (value != NULL) {
|
||||
copy_size = strlen(value);
|
||||
if (copy_size <= size) {
|
||||
memcpy(buf, value, copy_size);
|
||||
ret = 0;
|
||||
} else {
|
||||
LOGE("buffer is small too get %s.\n", key);
|
||||
}
|
||||
} else {
|
||||
LOGD("getstring (%s) fail.\n", key);
|
||||
}
|
||||
|
||||
iniparser_freedict(config_dic);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __CONFIG_SERVICE_H__
|
||||
#define __CONFIG_SERVICE_H__
|
||||
|
||||
int create_config_svc(char* config_path);
|
||||
void release_cofnig_svc();
|
||||
int get_config_value(const char *key, int default_value);
|
||||
int get_config_string(const char *key, char *buf, int size);
|
||||
|
||||
#endif //__CONFIG_SERVICE_H__
|
||||
@@ -0,0 +1,513 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@file dictionary.c
|
||||
@author N. Devillard
|
||||
@brief Implements a dictionary for string variables.
|
||||
|
||||
This module implements a simple dictionary object, i.e. a list
|
||||
of string/string associations. This object is useful to store e.g.
|
||||
informations retrieved from a configuration file (ini files).
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
//extern "C" {
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Includes
|
||||
---------------------------------------------------------------------------*/
|
||||
#include "dictionary.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/** Maximum value size for integers and doubles. */
|
||||
#define MAXVALSZ 1024
|
||||
|
||||
/** Minimal allocated number of entries in a dictionary */
|
||||
#define DICTMINSZ 128
|
||||
|
||||
/** Invalid key token */
|
||||
#define DICT_INVALID_KEY ((char*)-1)
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Private functions
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
/* Doubles the allocated size associated to a pointer */
|
||||
/* 'size' is the current allocated size. */
|
||||
static void * mem_double(void * ptr, int size)
|
||||
{
|
||||
void * newptr;
|
||||
|
||||
newptr = calloc(2 * size, 1);
|
||||
if (newptr == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(newptr, ptr, size);
|
||||
free(ptr);
|
||||
return newptr;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Duplicate a string
|
||||
@param s String to duplicate
|
||||
@return Pointer to a newly allocated string, to be freed with free()
|
||||
|
||||
This is a replacement for strdup(). This implementation is provided
|
||||
for systems that do not have it.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
static char * xstrdup(const char * s)
|
||||
{
|
||||
char * t;
|
||||
|
||||
if (!s)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
t = (char*)malloc(strlen(s) + 1);
|
||||
if (t)
|
||||
{
|
||||
strcpy(t, s);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Function codes
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Compute the hash key for a string.
|
||||
@param key Character string to use for key.
|
||||
@return 1 unsigned int on at least 32 bits.
|
||||
|
||||
This hash function has been taken from an Article in Dr Dobbs Journal.
|
||||
This is normally a collision-free function, distributing keys evenly.
|
||||
The key is stored anyway in the struct so that collision can be avoided
|
||||
by comparing the key itself in last resort.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
unsigned dictionary_hash(const char * key)
|
||||
{
|
||||
int len;
|
||||
unsigned hash;
|
||||
int i;
|
||||
|
||||
len = strlen(key);
|
||||
for (hash = 0, i = 0; i < len; i++)
|
||||
{
|
||||
hash += (unsigned)key[i];
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
}
|
||||
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >> 11);
|
||||
hash += (hash << 15);
|
||||
return hash;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Create a new dictionary object.
|
||||
@param size Optional initial size of the dictionary.
|
||||
@return 1 newly allocated dictionary objet.
|
||||
|
||||
This function allocates a new dictionary object of given size and returns
|
||||
it. If you do not know in advance (roughly) the number of entries in the
|
||||
dictionary, give size=0.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
dictionary * dictionary_new(int size)
|
||||
{
|
||||
dictionary * d;
|
||||
|
||||
/* If no size was specified, allocate space for DICTMINSZ */
|
||||
if (size < DICTMINSZ)
|
||||
{
|
||||
size = DICTMINSZ;
|
||||
}
|
||||
|
||||
if (!(d = (dictionary *)calloc(1, sizeof(dictionary))))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
d->size = size;
|
||||
d->val = (char **)calloc(size, sizeof(char*));
|
||||
d->key = (char **)calloc(size, sizeof(char*));
|
||||
d->hash = (unsigned int *)calloc(size, sizeof(unsigned));
|
||||
return d;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Delete a dictionary object
|
||||
@param d dictionary object to deallocate.
|
||||
@return void
|
||||
|
||||
Deallocate a dictionary object and all memory associated to it.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void dictionary_del(dictionary * d)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (d == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < d->size; i++)
|
||||
{
|
||||
if (d->key[i] != NULL)
|
||||
{
|
||||
free(d->key[i]);
|
||||
}
|
||||
|
||||
if (d->val[i] != NULL)
|
||||
{
|
||||
free(d->val[i]);
|
||||
}
|
||||
}
|
||||
|
||||
free(d->val);
|
||||
free(d->key);
|
||||
free(d->hash);
|
||||
free(d);
|
||||
return;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Get a value from a dictionary.
|
||||
@param d dictionary object to search.
|
||||
@param key Key to look for in the dictionary.
|
||||
@param def Default value to return if key not found.
|
||||
@return 1 pointer to internally allocated character string.
|
||||
|
||||
This function locates a key in a dictionary and returns a pointer to its
|
||||
value, or the passed 'def' pointer if no such key can be found in
|
||||
dictionary. The returned character pointer points to data internal to the
|
||||
dictionary object, you should not try to free it or modify it.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
char * dictionary_get(dictionary * d, const char * key, char * def)
|
||||
{
|
||||
unsigned hash;
|
||||
int i;
|
||||
|
||||
hash = dictionary_hash(key);
|
||||
for (i = 0; i < d->size; i++)
|
||||
{
|
||||
if (d->key[i] == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Compare hash */
|
||||
if (hash == d->hash[i])
|
||||
{
|
||||
/* Compare string, to avoid hash collisions */
|
||||
if (!strcmp(key, d->key[i]))
|
||||
{
|
||||
return d->val[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Set a value in a dictionary.
|
||||
@param d dictionary object to modify.
|
||||
@param key Key to modify or add.
|
||||
@param val Value to add.
|
||||
@return int 0 if Ok, anything else otherwise
|
||||
|
||||
If the given key is found in the dictionary, the associated value is
|
||||
replaced by the provided one. If the key cannot be found in the
|
||||
dictionary, it is added to it.
|
||||
|
||||
It is Ok to provide a NULL value for val, but NULL values for the dictionary
|
||||
or the key are considered as errors: the function will return immediately
|
||||
in such a case.
|
||||
|
||||
Notice that if you dictionary_set a variable to NULL, a call to
|
||||
dictionary_get will return a NULL value: the variable will be found, and
|
||||
its value (NULL) is returned. In other words, setting the variable
|
||||
content to NULL is equivalent to deleting the variable from the
|
||||
dictionary. It is not possible (in this implementation) to have a key in
|
||||
the dictionary without value.
|
||||
|
||||
This function returns non-zero in case of failure.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int dictionary_set(dictionary * d, const char * key, const char * val)
|
||||
{
|
||||
int i;
|
||||
unsigned hash;
|
||||
|
||||
if ((d == NULL) || (key == NULL))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Compute hash for this key */
|
||||
hash = dictionary_hash(key);
|
||||
|
||||
/* Find if value is already in dictionary */
|
||||
if (d->n > 0)
|
||||
{
|
||||
for (i = 0; i < d->size; i++)
|
||||
{
|
||||
if (d->key[i] == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hash == d->hash[i])
|
||||
{
|
||||
/* Same hash value */
|
||||
if (!strcmp(key, d->key[i]))
|
||||
{
|
||||
/* Same key */
|
||||
/* Found a value: modify and return */
|
||||
if (d->val[i] != NULL)
|
||||
{
|
||||
free(d->val[i]);
|
||||
}
|
||||
|
||||
d->val[i] = val ? xstrdup(val) : NULL;
|
||||
|
||||
/* Value has been modified: return */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Add a new value */
|
||||
/* See if dictionary needs to grow */
|
||||
if (d->n == d->size)
|
||||
{
|
||||
/* Reached maximum size: reallocate dictionary */
|
||||
d->val = (char **)mem_double(d->val, d->size * sizeof(char*));
|
||||
d->key = (char **)mem_double(d->key, d->size * sizeof(char*));
|
||||
d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned));
|
||||
if ((d->val == NULL) || (d->key == NULL) || (d->hash == NULL))
|
||||
{
|
||||
/* Cannot grow dictionary */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Double size */
|
||||
d->size *= 2;
|
||||
}
|
||||
|
||||
/* Insert key in the first empty slot. Start at d->n and wrap at
|
||||
d->size. Because d->n < d->size this will necessarily
|
||||
terminate. */
|
||||
for (i = d->n; d->key[i];)
|
||||
{
|
||||
if (++i == d->size)
|
||||
{
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy key */
|
||||
d->key[i] = xstrdup(key);
|
||||
d->val[i] = val ? xstrdup(val) : NULL;
|
||||
d->hash[i] = hash;
|
||||
d->n++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Delete a key in a dictionary
|
||||
@param d dictionary object to modify.
|
||||
@param key Key to remove.
|
||||
@return void
|
||||
|
||||
This function deletes a key in a dictionary. Nothing is done if the
|
||||
key cannot be found.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void dictionary_unset(dictionary * d, const char * key)
|
||||
{
|
||||
unsigned hash;
|
||||
int i;
|
||||
|
||||
if (key == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
hash = dictionary_hash(key);
|
||||
for (i = 0; i < d->size; i++)
|
||||
{
|
||||
if (d->key[i] == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Compare hash */
|
||||
if (hash == d->hash[i])
|
||||
{
|
||||
/* Compare string, to avoid hash collisions */
|
||||
if (!strcmp(key, d->key[i]))
|
||||
{
|
||||
/* Found key */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= d->size)
|
||||
{
|
||||
/* Key not found */
|
||||
return;
|
||||
}
|
||||
|
||||
free(d->key[i]);
|
||||
d->key[i] = NULL;
|
||||
if (d->val[i] != NULL)
|
||||
{
|
||||
free(d->val[i]);
|
||||
d->val[i] = NULL;
|
||||
}
|
||||
|
||||
d->hash[i] = 0;
|
||||
d->n--;
|
||||
return;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Dump a dictionary to an opened file pointer.
|
||||
@param d Dictionary to dump
|
||||
@param f Opened file pointer.
|
||||
@return void
|
||||
|
||||
Dumps a dictionary onto an opened file pointer. Key pairs are printed out
|
||||
as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
|
||||
output file pointers.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void dictionary_dump(dictionary * d, FILE * out)
|
||||
{
|
||||
int i;
|
||||
|
||||
if ((d == NULL) || (out == NULL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (d->n < 1)
|
||||
{
|
||||
fprintf(out, "empty dictionary\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < d->size; i++)
|
||||
{
|
||||
if (d->key[i])
|
||||
{
|
||||
fprintf(out, "%20s\t[%s]\n",
|
||||
d->key[i],
|
||||
d->val[i] ? d->val[i] : "UNDEF");
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Test code */
|
||||
#ifdef TESTDIC
|
||||
#define NVALS 20000
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
dictionary * d;
|
||||
char * val;
|
||||
int i;
|
||||
char cval[90];
|
||||
|
||||
/* Allocate dictionary */
|
||||
printf("allocating...\n");
|
||||
d = dictionary_new(0);
|
||||
|
||||
/* Set values in dictionary */
|
||||
printf("setting %d values...\n", NVALS);
|
||||
for (i = 0; i < NVALS; i++)
|
||||
{
|
||||
sprintf(cval, "%04d", i);
|
||||
dictionary_set(d, cval, "salut");
|
||||
}
|
||||
|
||||
printf("getting %d values...\n", NVALS);
|
||||
for (i = 0; i < NVALS; i++)
|
||||
{
|
||||
sprintf(cval, "%04d", i);
|
||||
val = dictionary_get(d, cval, DICT_INVALID_KEY);
|
||||
if (val == DICT_INVALID_KEY)
|
||||
{
|
||||
printf("cannot get value for key [%s]\n", cval);
|
||||
}
|
||||
}
|
||||
|
||||
printf("unsetting %d values...\n", NVALS);
|
||||
for (i = 0; i < NVALS; i++)
|
||||
{
|
||||
sprintf(cval, "%04d", i);
|
||||
dictionary_unset(d, cval);
|
||||
}
|
||||
|
||||
if (d->n != 0)
|
||||
{
|
||||
printf("error deleting values\n");
|
||||
}
|
||||
|
||||
printf("deallocating...\n");
|
||||
dictionary_del(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* vim: set ts=4 et sw=4 tw=75 */
|
||||
|
||||
//}//end if extern "C"
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@file dictionary.h
|
||||
@author N. Devillard
|
||||
@brief Implements a dictionary for string variables.
|
||||
|
||||
This module implements a simple dictionary object, i.e. a list
|
||||
of string/string associations. This object is useful to store e.g.
|
||||
informations retrieved from a configuration file (ini files).
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef _DICTIONARY_H_
|
||||
#define _DICTIONARY_H_
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Includes
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
New types
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Dictionary object
|
||||
|
||||
This object contains a list of string/string associations. Each
|
||||
association is identified by a unique string key. Looking up values
|
||||
in the dictionary is speeded up by the use of a (hopefully collision-free)
|
||||
hash function.
|
||||
*/
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
typedef struct _dictionary_
|
||||
{
|
||||
int n; /** Number of entries in dictionary */
|
||||
int size; /** Storage size */
|
||||
char ** val; /** List of string values */
|
||||
char ** key; /** List of string keys */
|
||||
unsigned * hash; /** List of hash values for keys */
|
||||
} dictionary;
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Function prototypes
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Compute the hash key for a string.
|
||||
@param key Character string to use for key.
|
||||
@return 1 unsigned int on at least 32 bits.
|
||||
|
||||
This hash function has been taken from an Article in Dr Dobbs Journal.
|
||||
This is normally a collision-free function, distributing keys evenly.
|
||||
The key is stored anyway in the struct so that collision can be avoided
|
||||
by comparing the key itself in last resort.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
unsigned dictionary_hash(const char * key);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Create a new dictionary object.
|
||||
@param size Optional initial size of the dictionary.
|
||||
@return 1 newly allocated dictionary objet.
|
||||
|
||||
This function allocates a new dictionary object of given size and returns
|
||||
it. If you do not know in advance (roughly) the number of entries in the
|
||||
dictionary, give size=0.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
dictionary * dictionary_new(int size);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Delete a dictionary object
|
||||
@param d dictionary object to deallocate.
|
||||
@return void
|
||||
|
||||
Deallocate a dictionary object and all memory associated to it.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void dictionary_del(dictionary * vd);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Get a value from a dictionary.
|
||||
@param d dictionary object to search.
|
||||
@param key Key to look for in the dictionary.
|
||||
@param def Default value to return if key not found.
|
||||
@return 1 pointer to internally allocated character string.
|
||||
|
||||
This function locates a key in a dictionary and returns a pointer to its
|
||||
value, or the passed 'def' pointer if no such key can be found in
|
||||
dictionary. The returned character pointer points to data internal to the
|
||||
dictionary object, you should not try to free it or modify it.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
char * dictionary_get(dictionary * d, const char * key, char * def);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Set a value in a dictionary.
|
||||
@param d dictionary object to modify.
|
||||
@param key Key to modify or add.
|
||||
@param val Value to add.
|
||||
@return int 0 if Ok, anything else otherwise
|
||||
|
||||
If the given key is found in the dictionary, the associated value is
|
||||
replaced by the provided one. If the key cannot be found in the
|
||||
dictionary, it is added to it.
|
||||
|
||||
It is Ok to provide a NULL value for val, but NULL values for the dictionary
|
||||
or the key are considered as errors: the function will return immediately
|
||||
in such a case.
|
||||
|
||||
Notice that if you dictionary_set a variable to NULL, a call to
|
||||
dictionary_get will return a NULL value: the variable will be found, and
|
||||
its value (NULL) is returned. In other words, setting the variable
|
||||
content to NULL is equivalent to deleting the variable from the
|
||||
dictionary. It is not possible (in this implementation) to have a key in
|
||||
the dictionary without value.
|
||||
|
||||
This function returns non-zero in case of failure.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int dictionary_set(dictionary * vd, const char * key, const char * val);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Delete a key in a dictionary
|
||||
@param d dictionary object to modify.
|
||||
@param key Key to remove.
|
||||
@return void
|
||||
|
||||
This function deletes a key in a dictionary. Nothing is done if the
|
||||
key cannot be found.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void dictionary_unset(dictionary * d, const char * key);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Dump a dictionary to an opened file pointer.
|
||||
@param d Dictionary to dump
|
||||
@param f Opened file pointer.
|
||||
@return void
|
||||
|
||||
Dumps a dictionary onto an opened file pointer. Key pairs are printed out
|
||||
as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
|
||||
output file pointers.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void dictionary_dump(dictionary * d, FILE * out);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <semaphore.h>
|
||||
|
||||
static sem_t g_sem;
|
||||
|
||||
int events_init(void)
|
||||
{
|
||||
return sem_init(&g_sem, 0, 0);
|
||||
}
|
||||
|
||||
int events_wait(void)
|
||||
{
|
||||
return sem_wait(&g_sem);
|
||||
}
|
||||
|
||||
int events_wait_timeout_ms(unsigned long long timeout_ms)
|
||||
{
|
||||
int rc;
|
||||
unsigned long long sec, nsec;
|
||||
struct timespec tv = {0};
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &tv);
|
||||
|
||||
sec = timeout_ms / 1000;
|
||||
nsec = (timeout_ms % 1000) * 1000000;
|
||||
|
||||
tv.tv_nsec += nsec;
|
||||
tv.tv_sec += sec;
|
||||
|
||||
rc = sem_timedwait(&g_sem, (const struct timespec*)&tv);
|
||||
if (rc < 0) {
|
||||
if (rc == ETIMEDOUT) {
|
||||
printf("sem_timedwait timeout.\n");
|
||||
} else {
|
||||
printf("sem_timedwait fail.\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int events_notity_one(void)
|
||||
{
|
||||
return sem_post(&g_sem);
|
||||
}
|
||||
|
||||
int events_deinit(void)
|
||||
{
|
||||
return sem_destroy(&g_sem);
|
||||
}
|
||||
|
||||
int node_wait(void)
|
||||
{
|
||||
return events_wait();
|
||||
}
|
||||
|
||||
int node_wait_timeout_ms(unsigned long long timeout_ms)
|
||||
{
|
||||
return events_wait_timeout_ms(timeout_ms);
|
||||
}
|
||||
|
||||
int node_notity_one(void)
|
||||
{
|
||||
return events_notity_one();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __EVENTS_H_
|
||||
#define __EVENTS_H_
|
||||
|
||||
int events_init(void);
|
||||
int events_deinit(void);
|
||||
int events_wait(void);
|
||||
int events_wait_timeout_ms(unsigned long long timeout_ms);
|
||||
int events_notity_one(void);
|
||||
|
||||
int node_wait(void);
|
||||
int node_wait_timeout_ms(unsigned long long timeout_ms);
|
||||
int node_notity_one(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,504 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
/*
|
||||
* The main logic about frame cache module
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include "log_base.h"
|
||||
#include "events.h"
|
||||
#include "config_svc.h"
|
||||
#include "frame_cache.h"
|
||||
|
||||
static void cache_init(frame_cache_t *cache)
|
||||
{
|
||||
cache->tail = NULL;
|
||||
cache->head = NULL;
|
||||
cache->count = 0;
|
||||
}
|
||||
|
||||
static void cache_destroy(frame_cache_t *cache)
|
||||
{
|
||||
frame_node_t *tmp = NULL;
|
||||
|
||||
while (cache->tail != NULL) {
|
||||
tmp = cache->tail;
|
||||
cache->tail = tmp->next;
|
||||
free(tmp->mem);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
cache->tail = NULL;
|
||||
cache->head = NULL;
|
||||
cache->count = 0;
|
||||
}
|
||||
|
||||
static int put_frame_into_cache(frame_cache_t *cache, frame_node_t *node)
|
||||
{
|
||||
node->next = NULL;
|
||||
cache->count++;
|
||||
|
||||
// first time
|
||||
if (cache->head == NULL && cache->tail == NULL) {
|
||||
cache->head = node;
|
||||
cache->tail = node;
|
||||
} else {
|
||||
cache->head->next = node;
|
||||
cache->head = node;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_frame_from_cache(frame_cache_t *cache, frame_node_t **node)
|
||||
{
|
||||
*node = cache->tail;
|
||||
|
||||
if ((cache->tail != NULL) && (cache->tail->next != NULL)) {
|
||||
cache->tail = cache->tail->next;
|
||||
cache->count--;
|
||||
} else if (cache->tail == cache->head) {
|
||||
cache->tail = NULL;
|
||||
cache->head = NULL;
|
||||
cache->count = 0;
|
||||
}
|
||||
|
||||
return (*node != NULL) ? 0 : -1;
|
||||
}
|
||||
|
||||
static int free_queue(frame_queue_t *queue)
|
||||
{
|
||||
frame_cache_t *cache = queue->cache;
|
||||
|
||||
if (cache != NULL) {
|
||||
cache_destroy(cache);
|
||||
free(cache);
|
||||
queue->cache = NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&(queue->locker));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int free_uvc_cache(uvc_cache_t *uvc_cache)
|
||||
{
|
||||
/* free queue*/
|
||||
free_queue(uvc_cache->ok_queue);
|
||||
free_queue(uvc_cache->free_queue);
|
||||
|
||||
free(uvc_cache->ok_queue);
|
||||
free(uvc_cache->free_queue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int free_uac_cache(uac_cache_t *uac_cache)
|
||||
{
|
||||
/* free queue*/
|
||||
free_queue(uac_cache->ok_queue);
|
||||
free_queue(uac_cache->free_queue);
|
||||
|
||||
free(uac_cache->ok_queue);
|
||||
free(uac_cache->free_queue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int init_queue(frame_queue_t **queue)
|
||||
{
|
||||
frame_queue_t *q = *queue;
|
||||
|
||||
if (q == NULL) {
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
q->cache = (frame_cache_t *)malloc(sizeof(frame_cache_t));
|
||||
if (q->cache == NULL) {
|
||||
LOGE("malloc frame cache failure\n");
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
cache_init(q->cache);
|
||||
|
||||
/*FIXME: recursive locker....*/
|
||||
pthread_mutex_init(&(q->locker), NULL);
|
||||
|
||||
return 0;
|
||||
|
||||
ERR:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int init_uvc_frame_cache(uvc_cache_t **cache)
|
||||
{
|
||||
uvc_cache_t *uvc_cache = *cache;
|
||||
|
||||
if (uvc_cache == NULL) {
|
||||
LOGE("the uvc cache is null\n");
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
uvc_cache->ok_queue = NULL;
|
||||
uvc_cache->free_queue = NULL;
|
||||
|
||||
uvc_cache->ok_queue = (frame_queue_t *)malloc(sizeof(frame_queue_t));
|
||||
if (uvc_cache->ok_queue == NULL) {
|
||||
LOGE("malloc ok_queue failure \n");
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
uvc_cache->free_queue = (frame_queue_t *)malloc(sizeof(frame_queue_t));
|
||||
if (uvc_cache->free_queue == NULL) {
|
||||
LOGE("malloc free_queue failure\n");
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
if (init_queue(&(uvc_cache->ok_queue)) < 0) {
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
if (init_queue(&(uvc_cache->free_queue)) < 0) {
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
ERR:
|
||||
if (uvc_cache->ok_queue != NULL) {
|
||||
free(uvc_cache->ok_queue);
|
||||
uvc_cache->ok_queue = NULL;
|
||||
}
|
||||
|
||||
if (uvc_cache->free_queue != NULL) {
|
||||
free(uvc_cache->free_queue);
|
||||
uvc_cache->free_queue = NULL;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int init_uac_frame_cache(uac_cache_t **cache)
|
||||
{
|
||||
uac_cache_t *uac_cache = *cache;
|
||||
|
||||
if (uac_cache == NULL) {
|
||||
LOGE("the uac cache is null\n");
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
uac_cache->ok_queue = NULL;
|
||||
uac_cache->free_queue = NULL;
|
||||
|
||||
uac_cache->ok_queue = (frame_queue_t *)malloc(sizeof(frame_queue_t));
|
||||
if (uac_cache->ok_queue == NULL) {
|
||||
LOGE("malloc ok_queue failure \n");
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
uac_cache->free_queue = (frame_queue_t *)malloc(sizeof(frame_queue_t));
|
||||
if (uac_cache->free_queue == NULL) {
|
||||
LOGE("malloc free_queue failure\n");
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
if (init_queue(&(uac_cache->ok_queue)) < 0) {
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
if (init_queue(&(uac_cache->free_queue)) < 0) {
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
ERR:
|
||||
if (uac_cache->ok_queue != NULL) {
|
||||
free(uac_cache->ok_queue);
|
||||
uac_cache->ok_queue = NULL;
|
||||
}
|
||||
|
||||
if (uac_cache->free_queue != NULL) {
|
||||
free(uac_cache->free_queue);
|
||||
uac_cache->free_queue = NULL;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void clear_frame_cache(frame_cache_t *c)
|
||||
{
|
||||
cache_destroy(c);
|
||||
}
|
||||
|
||||
static void clear_queue(frame_queue_t *q)
|
||||
{
|
||||
if (0 != pthread_mutex_lock(&(q->locker))) {
|
||||
LOGE("failed to lock frame cache\n");
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
clear_frame_cache(q->cache);
|
||||
|
||||
pthread_mutex_unlock(&(q->locker));
|
||||
|
||||
ERR:
|
||||
return;
|
||||
}
|
||||
|
||||
static int create_cache_node_list(frame_queue_t *q, unsigned int buffer_size, int cache_count)
|
||||
{
|
||||
int i = 0;
|
||||
unsigned int page_size;
|
||||
frame_node_t *n = NULL;
|
||||
|
||||
page_size = getpagesize();
|
||||
|
||||
buffer_size = (buffer_size + page_size - 1) & ~(page_size - 1);
|
||||
|
||||
for (; i < cache_count; ++i) {
|
||||
n = (frame_node_t *)malloc(sizeof(frame_node_t));
|
||||
if (!n) {
|
||||
LOGE("failed to malloc frame_node_t\n");
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
n->mem = (unsigned char *)memalign(page_size, buffer_size);
|
||||
if (!n->mem) {
|
||||
LOGE("failed to malloc frame_node_t->mem field\n");
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
n->length = buffer_size;
|
||||
n->used = 0;
|
||||
n->next = NULL;
|
||||
n->index = i;
|
||||
|
||||
put_node_to_queue(q, n);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
ERR:
|
||||
|
||||
if (n) {
|
||||
free(n);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//only one instance for each process.
|
||||
static uvc_cache_t *g_uvc_cache = NULL;
|
||||
static uac_cache_t *g_uac_cache = NULL;
|
||||
|
||||
uvc_cache_t *uvc_cache_get(void)
|
||||
{
|
||||
return g_uvc_cache;
|
||||
}
|
||||
|
||||
int create_uvc_cache(void)
|
||||
{
|
||||
unsigned int buffer_size;
|
||||
int cache_count;
|
||||
|
||||
g_uvc_cache = (uvc_cache_t *)malloc(sizeof(uvc_cache_t));
|
||||
if (g_uvc_cache == NULL) {
|
||||
LOGE("malloc uvc_cache failure\n");
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
if (init_uvc_frame_cache(&g_uvc_cache) < 0) {
|
||||
LOGE("init uvc cache failure\n");
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
buffer_size = get_config_value("uvc:imagesize", 1843200);
|
||||
cache_count = get_config_value("uvc:cache_count", 6);
|
||||
|
||||
create_cache_node_list(g_uvc_cache->free_queue, buffer_size, cache_count);
|
||||
|
||||
events_init();
|
||||
|
||||
return 0;
|
||||
|
||||
ERR:
|
||||
if (g_uvc_cache != NULL) {
|
||||
free(g_uvc_cache);
|
||||
g_uvc_cache = NULL;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void destroy_uvc_cache(void)
|
||||
{
|
||||
if (uvc_cache_get() != NULL) {
|
||||
free_uvc_cache(uvc_cache_get());
|
||||
free(uvc_cache_get());
|
||||
events_deinit();
|
||||
|
||||
g_uvc_cache = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void clear_uvc_cache(void)
|
||||
{
|
||||
if (uvc_cache_get() != NULL) {
|
||||
uvc_cache_t* uvc_cache = uvc_cache_get();
|
||||
|
||||
clear_queue(uvc_cache->free_queue);
|
||||
clear_queue(uvc_cache->ok_queue);
|
||||
}
|
||||
}
|
||||
|
||||
uac_cache_t* uac_cache_get(void)
|
||||
{
|
||||
return g_uac_cache;
|
||||
}
|
||||
|
||||
int create_uac_cache(void)
|
||||
{
|
||||
int cache_count;
|
||||
unsigned int buffer_size;
|
||||
|
||||
g_uac_cache = (uac_cache_t *)malloc(sizeof(uac_cache_t));
|
||||
|
||||
if (g_uac_cache == NULL) {
|
||||
LOGE("malloc uac_cache failure\n");
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
if (init_uac_frame_cache(&g_uac_cache) < 0) {
|
||||
LOGE("init uac cache failure\n");
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
buffer_size = get_config_value("uac:imagesize", 1024);
|
||||
cache_count = get_config_value("uac:cache_count", 6);
|
||||
|
||||
create_cache_node_list(g_uac_cache->free_queue, buffer_size, cache_count);
|
||||
|
||||
return 0;
|
||||
|
||||
ERR:
|
||||
if (g_uac_cache != NULL) {
|
||||
free(g_uac_cache);
|
||||
}
|
||||
|
||||
g_uac_cache = NULL;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void destroy_uac_cache(void)
|
||||
{
|
||||
if (uac_cache_get() != NULL) {
|
||||
free_uac_cache(uac_cache_get());
|
||||
free(uac_cache_get());
|
||||
|
||||
g_uac_cache = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void clear_uac_cache(void)
|
||||
{
|
||||
if (uac_cache_get() != NULL) {
|
||||
uac_cache_t * uac_cache = uac_cache_get();
|
||||
|
||||
clear_queue(uac_cache->free_queue);
|
||||
clear_queue(uac_cache->ok_queue);
|
||||
}
|
||||
}
|
||||
|
||||
int put_node_to_queue(frame_queue_t *q, frame_node_t* node)
|
||||
{
|
||||
if ((q == NULL) || (node == NULL)) {
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
if (0 != pthread_mutex_lock(&(q->locker))) {
|
||||
LOGE("failed to lock frame cache\n");
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
if (0 != put_frame_into_cache(q->cache, node)) {
|
||||
pthread_mutex_unlock(&(q->locker));
|
||||
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&(q->locker));
|
||||
|
||||
if (g_uvc_cache && g_uvc_cache->ok_queue == q) {
|
||||
node_notity_one();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
ERR:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int get_node_from_queue(frame_queue_t *q, frame_node_t** node)
|
||||
{
|
||||
if ((q == NULL) || (node == NULL)) {
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
if (0 != pthread_mutex_lock(&(q->locker))) {
|
||||
LOGE("failed to lock frame cache\n");
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
if (0 != get_frame_from_cache(q->cache, node)) {
|
||||
pthread_mutex_unlock(&(q->locker));
|
||||
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&(q->locker));
|
||||
|
||||
return 0;
|
||||
ERR:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int wait_queue(frame_queue_t *q)
|
||||
{
|
||||
if (q == NULL) {
|
||||
goto ERR;
|
||||
}
|
||||
|
||||
ERR:
|
||||
return -1;
|
||||
}
|
||||
|
||||
void debug_dump_node(frame_node_t *node)
|
||||
{
|
||||
printf("0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
|
||||
node->mem[0],
|
||||
node->mem[1],
|
||||
node->mem[2],
|
||||
node->mem[3],
|
||||
node->mem[4],
|
||||
node->mem[5]);
|
||||
}
|
||||
|
||||
void clear_ok_queue(void)
|
||||
{
|
||||
frame_node_t* node = NULL;
|
||||
uvc_cache_t* uvc_cache = uvc_cache_get();
|
||||
|
||||
events_init();
|
||||
while (0 == get_node_from_queue(uvc_cache->ok_queue, &node)) {
|
||||
node->used = 0;
|
||||
put_node_to_queue(uvc_cache->free_queue, node);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
/*
|
||||
* The header file about frame cache
|
||||
*/
|
||||
|
||||
#ifndef __FRAME_CACHE_H__
|
||||
#define __FRAME_CACHE_H__
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
typedef struct frame_node_t {
|
||||
unsigned char *mem;
|
||||
unsigned int length;
|
||||
unsigned int used;
|
||||
unsigned int index;
|
||||
unsigned int offset;
|
||||
struct frame_node_t *next;
|
||||
} frame_node_t;
|
||||
|
||||
typedef struct frame_cache_t {
|
||||
struct frame_node_t *head;
|
||||
struct frame_node_t *tail;
|
||||
unsigned int count;
|
||||
} frame_cache_t;
|
||||
|
||||
typedef struct frame_queue_t {
|
||||
struct frame_cache_t *cache;
|
||||
pthread_mutex_t locker;
|
||||
} frame_queue_t;
|
||||
|
||||
typedef struct uvc_cache_t {
|
||||
frame_queue_t *ok_queue;
|
||||
frame_queue_t *free_queue;
|
||||
} uvc_cache_t;
|
||||
|
||||
typedef struct uac_cache_t {
|
||||
frame_queue_t *ok_queue;
|
||||
frame_queue_t *free_queue;
|
||||
} uac_cache_t;
|
||||
|
||||
int create_uvc_cache(void);
|
||||
void destroy_uvc_cache(void);
|
||||
uvc_cache_t *uvc_cache_get(void);
|
||||
|
||||
int create_uac_cache(void);
|
||||
void destroy_uac_cache(void);
|
||||
uac_cache_t *uac_cache_get(void);
|
||||
|
||||
int put_node_to_queue(frame_queue_t *q, frame_node_t *node);
|
||||
int get_node_from_queue(frame_queue_t *q, frame_node_t **node);
|
||||
void clear_uvc_cache(void);
|
||||
void debug_dump_node(frame_node_t *node);
|
||||
|
||||
void clear_ok_queue(void);
|
||||
|
||||
#endif //__FRAME_CACHE_H__
|
||||
@@ -0,0 +1,952 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
//extern "C" {
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@file iniparser.c
|
||||
@author N. Devillard
|
||||
@brief Parser for ini files.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
/*---------------------------- Includes ------------------------------------*/
|
||||
#include <ctype.h>
|
||||
#include "iniparser.h"
|
||||
|
||||
/*---------------------------- Defines -------------------------------------*/
|
||||
#define ASCIILINESZ (1024)
|
||||
#define INI_INVALID_KEY ((char*)-1)
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Private to this module
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* This enum stores the status for each parsed line (internal use only).
|
||||
*/
|
||||
typedef enum _line_status_
|
||||
{
|
||||
LINE_UNPROCESSED,
|
||||
LINE_ERROR,
|
||||
LINE_EMPTY,
|
||||
LINE_COMMENT,
|
||||
LINE_SECTION,
|
||||
LINE_VALUE
|
||||
} line_status;
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Convert a string to lowercase.
|
||||
@param s String to convert.
|
||||
@return ptr to statically allocated string.
|
||||
|
||||
This function returns a pointer to a statically allocated string
|
||||
containing a lowercased version of the input string. Do not free
|
||||
or modify the returned string! Since the returned string is statically
|
||||
allocated, it will be modified at each function call (not re-entrant).
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
static char * strlwc(const char * s)
|
||||
{
|
||||
static char l[ASCIILINESZ + 1];
|
||||
int i;
|
||||
|
||||
if (s == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(l, 0, ASCIILINESZ + 1);
|
||||
i = 0;
|
||||
while (s[i] && (i < ASCIILINESZ))
|
||||
{
|
||||
l[i] = (char)tolower((int)s[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
l[ASCIILINESZ] = (char)0;
|
||||
return l;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Remove blanks at the beginning and the end of a string.
|
||||
@param s String to parse.
|
||||
@return ptr to statically allocated string.
|
||||
|
||||
This function returns a pointer to a statically allocated string,
|
||||
which is identical to the input string, except that all blank
|
||||
characters at the end and the beg. of the string have been removed.
|
||||
Do not free or modify the returned string! Since the returned string
|
||||
is statically allocated, it will be modified at each function call
|
||||
(not re-entrant).
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
static char * strstrip(const char * s)
|
||||
{
|
||||
static char l[ASCIILINESZ + 1];
|
||||
char * last;
|
||||
|
||||
if (s == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (isspace((int)*s) && *s)
|
||||
{
|
||||
s++;
|
||||
}
|
||||
|
||||
memset(l, 0, ASCIILINESZ + 1);
|
||||
strcpy(l, s);
|
||||
last = l + strlen(l);
|
||||
while (last > l)
|
||||
{
|
||||
if (!isspace((int)*(last - 1)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
last--;
|
||||
}
|
||||
|
||||
*last = (char)0;
|
||||
return (char*)l;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Get number of sections in a dictionary
|
||||
@param d Dictionary to examine
|
||||
@return int Number of sections found in dictionary
|
||||
|
||||
This function returns the number of sections found in a dictionary.
|
||||
The test to recognize sections is done on the string stored in the
|
||||
dictionary: a section name is given as "section" whereas a key is
|
||||
stored as "section:key", thus the test looks for entries that do not
|
||||
contain a colon.
|
||||
|
||||
This clearly fails in the case a section name contains a colon, but
|
||||
this should simply be avoided.
|
||||
|
||||
This function returns -1 in case of error.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_getnsec(dictionary * d)
|
||||
{
|
||||
int i;
|
||||
int nsec;
|
||||
|
||||
if (d == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
nsec = 0;
|
||||
for (i = 0; i < d->size; i++)
|
||||
{
|
||||
if (d->key[i] == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strchr(d->key[i], ':') == NULL)
|
||||
{
|
||||
nsec++;
|
||||
}
|
||||
}
|
||||
|
||||
return nsec;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Get name for section n in a dictionary.
|
||||
@param d Dictionary to examine
|
||||
@param n Section number (from 0 to nsec-1).
|
||||
@return Pointer to char string
|
||||
|
||||
This function locates the n-th section in a dictionary and returns
|
||||
its name as a pointer to a string statically allocated inside the
|
||||
dictionary. Do not free or modify the returned string!
|
||||
|
||||
This function returns NULL in case of error.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
char * iniparser_getsecname(dictionary * d, int n)
|
||||
{
|
||||
int i;
|
||||
int foundsec;
|
||||
|
||||
if ((d == NULL) || (n < 0))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
foundsec = 0;
|
||||
for (i = 0; i < d->size; i++)
|
||||
{
|
||||
if (d->key[i] == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strchr(d->key[i], ':') == NULL)
|
||||
{
|
||||
foundsec++;
|
||||
if (foundsec > n)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (foundsec <= n)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return d->key[i];
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Dump a dictionary to an opened file pointer.
|
||||
@param d Dictionary to dump.
|
||||
@param f Opened file pointer to dump to.
|
||||
@return void
|
||||
|
||||
This function prints out the contents of a dictionary, one element by
|
||||
line, onto the provided file pointer. It is OK to specify @c stderr
|
||||
or @c stdout as output files. This function is meant for debugging
|
||||
purposes mostly.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void iniparser_dump(dictionary * d, FILE * f)
|
||||
{
|
||||
int i;
|
||||
|
||||
if ((d == NULL) || (f == NULL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < d->size; i++)
|
||||
{
|
||||
if (d->key[i] == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (d->val[i] != NULL)
|
||||
{
|
||||
fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(f, "[%s]=UNDEF\n", d->key[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Save a dictionary to a loadable ini file
|
||||
@param d Dictionary to dump
|
||||
@param f Opened file pointer to dump to
|
||||
@return void
|
||||
|
||||
This function dumps a given dictionary into a loadable ini file.
|
||||
It is Ok to specify @c stderr or @c stdout as output files.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void iniparser_dump_ini(dictionary * d, FILE * f)
|
||||
{
|
||||
int i;
|
||||
int nsec;
|
||||
char * secname;
|
||||
|
||||
if ((d == NULL) || (f == NULL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
nsec = iniparser_getnsec(d);
|
||||
if (nsec < 1)
|
||||
{
|
||||
/* No section in file: dump all keys as they are */
|
||||
for (i = 0; i < d->size; i++)
|
||||
{
|
||||
if (d->key[i] == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(f, "%s = %s\n", d->key[i], d->val[i]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < nsec; i++)
|
||||
{
|
||||
secname = iniparser_getsecname(d, i);
|
||||
iniparser_dumpsection_ini(d, secname, f);
|
||||
}
|
||||
|
||||
fprintf(f, "\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Save a dictionary section to a loadable ini file
|
||||
@param d Dictionary to dump
|
||||
@param s Section name of dictionary to dump
|
||||
@param f Opened file pointer to dump to
|
||||
@return void
|
||||
|
||||
This function dumps a given section of a given dictionary into a loadable ini
|
||||
file. It is Ok to specify @c stderr or @c stdout as output files.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f)
|
||||
{
|
||||
int j;
|
||||
char keym[ASCIILINESZ + 1];
|
||||
int seclen;
|
||||
|
||||
if ((d == NULL) || (f == NULL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!iniparser_find_entry(d, s))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
seclen = (int)strlen(s);
|
||||
fprintf(f, "\n[%s]\n", s);
|
||||
sprintf(keym, "%s:", s);
|
||||
for (j = 0; j < d->size; j++)
|
||||
{
|
||||
if (d->key[j] == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strncmp(d->key[j], keym, seclen + 1))
|
||||
{
|
||||
fprintf(f,
|
||||
"%-30s = %s\n",
|
||||
d->key[j] + seclen + 1,
|
||||
d->val[j] ? d->val[j] : "");
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(f, "\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Get the number of keys in a section of a dictionary.
|
||||
@param d Dictionary to examine
|
||||
@param s Section name of dictionary to examine
|
||||
@return Number of keys in section
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_getsecnkeys(dictionary * d, char * s)
|
||||
{
|
||||
int seclen, nkeys;
|
||||
char keym[ASCIILINESZ + 1];
|
||||
int j;
|
||||
|
||||
nkeys = 0;
|
||||
|
||||
if (d == NULL)
|
||||
{
|
||||
return nkeys;
|
||||
}
|
||||
|
||||
if (!iniparser_find_entry(d, s))
|
||||
{
|
||||
return nkeys;
|
||||
}
|
||||
|
||||
seclen = (int)strlen(s);
|
||||
sprintf(keym, "%s:", s);
|
||||
|
||||
for (j = 0; j < d->size; j++)
|
||||
{
|
||||
if (d->key[j] == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strncmp(d->key[j], keym, seclen + 1))
|
||||
{
|
||||
nkeys++;
|
||||
}
|
||||
}
|
||||
|
||||
return nkeys;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Get the number of keys in a section of a dictionary.
|
||||
@param d Dictionary to examine
|
||||
@param s Section name of dictionary to examine
|
||||
@return pointer to statically allocated character strings
|
||||
|
||||
This function queries a dictionary and finds all keys in a given section.
|
||||
Each pointer in the returned char pointer-to-pointer is pointing to
|
||||
a string allocated in the dictionary; do not free or modify them.
|
||||
|
||||
This function returns NULL in case of error.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
char ** iniparser_getseckeys(dictionary * d, char * s)
|
||||
{
|
||||
char **keys;
|
||||
|
||||
int i, j;
|
||||
char keym[ASCIILINESZ + 1];
|
||||
int seclen, nkeys;
|
||||
|
||||
keys = NULL;
|
||||
|
||||
if (d == NULL)
|
||||
{
|
||||
return keys;
|
||||
}
|
||||
|
||||
if (!iniparser_find_entry(d, s))
|
||||
{
|
||||
return keys;
|
||||
}
|
||||
|
||||
nkeys = iniparser_getsecnkeys(d, s);
|
||||
|
||||
keys = (char**) malloc(nkeys * sizeof(char*));
|
||||
|
||||
seclen = (int)strlen(s);
|
||||
sprintf(keym, "%s:", s);
|
||||
|
||||
i = 0;
|
||||
|
||||
for (j = 0; j < d->size; j++)
|
||||
{
|
||||
if (d->key[j] == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strncmp(d->key[j], keym, seclen + 1))
|
||||
{
|
||||
keys[i] = d->key[j];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Get the string associated to a key
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param def Default value to return if key not found.
|
||||
@return pointer to statically allocated character string
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the pointer passed as 'def' is returned.
|
||||
The returned char pointer is pointing to a string allocated in
|
||||
the dictionary, do not free or modify it.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
char * iniparser_getstring(dictionary * d, const char * key, char * def)
|
||||
{
|
||||
char * lc_key;
|
||||
char * sval;
|
||||
|
||||
if ((d == NULL) || (key == NULL))
|
||||
{
|
||||
return def;
|
||||
}
|
||||
|
||||
lc_key = strlwc(key);
|
||||
sval = dictionary_get(d, lc_key, def);
|
||||
return sval;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Get the string associated to a key, convert to an int
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param notfound Value to return in case of error
|
||||
@return integer
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the notfound value is returned.
|
||||
|
||||
Supported values for integers include the usual C notation
|
||||
so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
|
||||
are supported. Examples:
|
||||
|
||||
"42" -> 42
|
||||
"042" -> 34 (octal -> decimal)
|
||||
"0x42" -> 66 (hexa -> decimal)
|
||||
|
||||
Warning: the conversion may overflow in various ways. Conversion is
|
||||
totally outsourced to strtol(), see the associated man page for overflow
|
||||
handling.
|
||||
|
||||
Credits: Thanks to A. Becker for suggesting strtol()
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_getint(dictionary * d, const char * key, int notfound)
|
||||
{
|
||||
char * str;
|
||||
|
||||
str = iniparser_getstring(d, key, INI_INVALID_KEY);
|
||||
if (str == INI_INVALID_KEY)
|
||||
{
|
||||
return notfound;
|
||||
}
|
||||
|
||||
return (int)strtol(str, NULL, 0);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Get the string associated to a key, convert to a double
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param notfound Value to return in case of error
|
||||
@return double
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the notfound value is returned.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
double iniparser_getdouble(dictionary * d, const char * key, double notfound)
|
||||
{
|
||||
char * str;
|
||||
|
||||
str = iniparser_getstring(d, key, INI_INVALID_KEY);
|
||||
if (str == INI_INVALID_KEY)
|
||||
{
|
||||
return notfound;
|
||||
}
|
||||
|
||||
return atof(str);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Get the string associated to a key, convert to a boolean
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param notfound Value to return in case of error
|
||||
@return integer
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the notfound value is returned.
|
||||
|
||||
A true boolean is found if one of the following is matched:
|
||||
|
||||
- A string starting with 'y'
|
||||
- A string starting with 'Y'
|
||||
- A string starting with 't'
|
||||
- A string starting with 'T'
|
||||
- A string starting with '1'
|
||||
|
||||
A false boolean is found if one of the following is matched:
|
||||
|
||||
- A string starting with 'n'
|
||||
- A string starting with 'N'
|
||||
- A string starting with 'f'
|
||||
- A string starting with 'F'
|
||||
- A string starting with '0'
|
||||
|
||||
The notfound value returned if no boolean is identified, does not
|
||||
necessarily have to be 0 or 1.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_getboolean(dictionary * d, const char * key, int notfound)
|
||||
{
|
||||
char * c;
|
||||
int ret;
|
||||
|
||||
c = iniparser_getstring(d, key, INI_INVALID_KEY);
|
||||
if (c == INI_INVALID_KEY)
|
||||
{
|
||||
return notfound;
|
||||
}
|
||||
|
||||
if ((c[0] == 'y') || (c[0] == 'Y') || (c[0] == '1') || (c[0] == 't') || (c[0] == 'T'))
|
||||
{
|
||||
ret = 1;
|
||||
}
|
||||
else if ((c[0] == 'n') || (c[0] == 'N') || (c[0] == '0') || (c[0] == 'f') || (c[0] == 'F'))
|
||||
{
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = notfound;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Finds out if a given entry exists in a dictionary
|
||||
@param ini Dictionary to search
|
||||
@param entry Name of the entry to look for
|
||||
@return integer 1 if entry exists, 0 otherwise
|
||||
|
||||
Finds out if a given entry exists in the dictionary. Since sections
|
||||
are stored as keys with NULL associated values, this is the only way
|
||||
of querying for the presence of sections in a dictionary.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_find_entry(dictionary * ini,
|
||||
const char * entry)
|
||||
{
|
||||
int found = 0;
|
||||
|
||||
if (iniparser_getstring(ini, entry, INI_INVALID_KEY) != INI_INVALID_KEY)
|
||||
{
|
||||
found = 1;
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Set an entry in a dictionary.
|
||||
@param ini Dictionary to modify.
|
||||
@param entry Entry to modify (entry name)
|
||||
@param val New value to associate to the entry.
|
||||
@return int 0 if Ok, -1 otherwise.
|
||||
|
||||
If the given entry can be found in the dictionary, it is modified to
|
||||
contain the provided value. If it cannot be found, -1 is returned.
|
||||
It is Ok to set val to NULL.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_set(dictionary * ini, const char * entry, const char * val)
|
||||
{
|
||||
return dictionary_set(ini, strlwc(entry), val);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Delete an entry in a dictionary
|
||||
@param ini Dictionary to modify
|
||||
@param entry Entry to delete (entry name)
|
||||
@return void
|
||||
|
||||
If the given entry can be found, it is deleted from the dictionary.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void iniparser_unset(dictionary * ini, const char * entry)
|
||||
{
|
||||
dictionary_unset(ini, strlwc(entry));
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Load a single line from an INI file
|
||||
@param input_line Input line, may be concatenated multi-line input
|
||||
@param section Output space to store section
|
||||
@param key Output space to store key
|
||||
@param value Output space to store value
|
||||
@return line_status value
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
static line_status iniparser_line(const char * input_line,
|
||||
char * section,
|
||||
char * key,
|
||||
char * value)
|
||||
{
|
||||
line_status sta;
|
||||
char line[ASCIILINESZ + 1];
|
||||
int len;
|
||||
|
||||
strcpy(line, strstrip(input_line));
|
||||
len = (int)strlen(line);
|
||||
|
||||
sta = LINE_UNPROCESSED;
|
||||
if (len < 1)
|
||||
{
|
||||
/* Empty line */
|
||||
sta = LINE_EMPTY;
|
||||
}
|
||||
else if ((line[0] == '#') || (line[0] == ';'))
|
||||
{
|
||||
/* Comment line */
|
||||
sta = LINE_COMMENT;
|
||||
}
|
||||
else if ((line[0] == '[') && (line[len - 1] == ']'))
|
||||
{
|
||||
/* Section name */
|
||||
sscanf(line, "[%[^]]", section);
|
||||
strcpy(section, strstrip(section));
|
||||
strcpy(section, strlwc(section));
|
||||
sta = LINE_SECTION;
|
||||
}
|
||||
else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
|
||||
|| sscanf (line, "%[^=] = '%[^\']'", key, value) == 2
|
||||
|| sscanf (line, "%[^=] = %[^;#]", key, value) == 2)
|
||||
{
|
||||
/* Usual key=value, with or without comments */
|
||||
strcpy(key, strstrip(key));
|
||||
strcpy(key, strlwc(key));
|
||||
strcpy(value, strstrip(value));
|
||||
|
||||
/*
|
||||
* sscanf cannot handle '' or "" as empty values
|
||||
* this is done here
|
||||
*/
|
||||
if (!strcmp(value, "\"\"") || (!strcmp(value, "''")))
|
||||
{
|
||||
value[0] = 0;
|
||||
}
|
||||
|
||||
sta = LINE_VALUE;
|
||||
}
|
||||
else if (sscanf(line, "%[^=] = %[;#]", key, value) == 2
|
||||
|| sscanf(line, "%[^=] %[=]", key, value) == 2)
|
||||
{
|
||||
/*
|
||||
* Special cases:
|
||||
* key=
|
||||
* key=;
|
||||
* key=#
|
||||
*/
|
||||
strcpy(key, strstrip(key));
|
||||
strcpy(key, strlwc(key));
|
||||
value[0] = 0;
|
||||
sta = LINE_VALUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Generate syntax error */
|
||||
sta = LINE_ERROR;
|
||||
}
|
||||
|
||||
return sta;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Parse an ini file and return an allocated dictionary object
|
||||
@param ininame Name of the ini file to read.
|
||||
@return Pointer to newly allocated dictionary
|
||||
|
||||
This is the parser for ini files. This function is called, providing
|
||||
the name of the file to be read. It returns a dictionary object that
|
||||
should not be accessed directly, but through accessor functions
|
||||
instead.
|
||||
|
||||
The returned dictionary must be freed using iniparser_freedict().
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
dictionary * iniparser_load(const char * ininame)
|
||||
{
|
||||
FILE * in;
|
||||
|
||||
char line [ASCIILINESZ + 1];
|
||||
char section [ASCIILINESZ/2];
|
||||
char key [ASCIILINESZ/2];
|
||||
char tmp [ASCIILINESZ + 1];
|
||||
char val [ASCIILINESZ + 1];
|
||||
|
||||
int last = 0;
|
||||
int len;
|
||||
int lineno = 0;
|
||||
int errs = 0;
|
||||
|
||||
dictionary * dict;
|
||||
|
||||
if ((in = fopen(ininame, "r")) == NULL)
|
||||
{
|
||||
fprintf(stderr, "iniparser: cannot open %s\n", ininame);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dict = dictionary_new(0);
|
||||
if (!dict)
|
||||
{
|
||||
fclose(in);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(line, 0, ASCIILINESZ);
|
||||
memset(section, 0, ASCIILINESZ/2);
|
||||
memset(key, 0, ASCIILINESZ/2);
|
||||
memset(val, 0, ASCIILINESZ);
|
||||
last = 0;
|
||||
|
||||
while (fgets(line + last, ASCIILINESZ - last, in) != NULL)
|
||||
{
|
||||
lineno++;
|
||||
len = (int)strlen(line) - 1;
|
||||
if (len == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Safety check against buffer overflows */
|
||||
if (line[len] != '\n')
|
||||
{
|
||||
fprintf(stderr,
|
||||
"iniparser: input line too long in %s (%d)\n",
|
||||
ininame,
|
||||
lineno);
|
||||
dictionary_del(dict);
|
||||
fclose(in);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get rid of \n and spaces at end of line */
|
||||
while ((len >= 0)
|
||||
&& ((line[len] == '\n') || (isspace(line[len]))))
|
||||
{
|
||||
line[len] = 0;
|
||||
len--;
|
||||
}
|
||||
|
||||
/* Detect multi-line */
|
||||
if (line[len] == '\\')
|
||||
{
|
||||
/* Multi-line value */
|
||||
last = len;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
last = 0;
|
||||
}
|
||||
|
||||
switch (iniparser_line(line, section, key, val))
|
||||
{
|
||||
case LINE_EMPTY:
|
||||
case LINE_COMMENT:
|
||||
break;
|
||||
|
||||
case LINE_SECTION:
|
||||
errs = dictionary_set(dict, section, NULL);
|
||||
break;
|
||||
|
||||
case LINE_VALUE:
|
||||
snprintf(tmp, sizeof(tmp), "%s:%s", section, key);
|
||||
errs = dictionary_set(dict, tmp, val);
|
||||
break;
|
||||
|
||||
case LINE_ERROR:
|
||||
fprintf(stderr, "iniparser: syntax error in %s (%d):\n",
|
||||
ininame,
|
||||
lineno);
|
||||
fprintf(stderr, "-> %s\n", line);
|
||||
errs++;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
memset(line, 0, ASCIILINESZ);
|
||||
last = 0;
|
||||
if (errs < 0)
|
||||
{
|
||||
fprintf(stderr, "iniparser: memory allocation failure\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (errs)
|
||||
{
|
||||
dictionary_del(dict);
|
||||
dict = NULL;
|
||||
}
|
||||
|
||||
fclose(in);
|
||||
return dict;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Free all memory associated to an ini dictionary
|
||||
@param d Dictionary to free
|
||||
@return void
|
||||
|
||||
Free all memory associated to an ini dictionary.
|
||||
It is mandatory to call this function before the dictionary object
|
||||
gets out of the current context.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void iniparser_freedict(dictionary * d)
|
||||
{
|
||||
dictionary_del(d);
|
||||
}
|
||||
|
||||
//} //end of extern "C"
|
||||
|
||||
/* vim: set ts=4 et sw=4 tw=75 */
|
||||
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@file iniparser.h
|
||||
@author N. Devillard
|
||||
@brief Parser for ini files.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef _INIPARSER_H_
|
||||
#define _INIPARSER_H_
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Includes
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* The following #include is necessary on many Unixes but not Linux.
|
||||
* It is not needed for Windows platforms.
|
||||
* Uncomment it if needed.
|
||||
*/
|
||||
|
||||
/* #include <unistd.h> */
|
||||
|
||||
#include "dictionary.h"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Get number of sections in a dictionary
|
||||
@param d Dictionary to examine
|
||||
@return int Number of sections found in dictionary
|
||||
|
||||
This function returns the number of sections found in a dictionary.
|
||||
The test to recognize sections is done on the string stored in the
|
||||
dictionary: a section name is given as "section" whereas a key is
|
||||
stored as "section:key", thus the test looks for entries that do not
|
||||
contain a colon.
|
||||
|
||||
This clearly fails in the case a section name contains a colon, but
|
||||
this should simply be avoided.
|
||||
|
||||
This function returns -1 in case of error.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
int iniparser_getnsec(dictionary * d);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Get name for section n in a dictionary.
|
||||
@param d Dictionary to examine
|
||||
@param n Section number (from 0 to nsec-1).
|
||||
@return Pointer to char string
|
||||
|
||||
This function locates the n-th section in a dictionary and returns
|
||||
its name as a pointer to a string statically allocated inside the
|
||||
dictionary. Do not free or modify the returned string!
|
||||
|
||||
This function returns NULL in case of error.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
char * iniparser_getsecname(dictionary * d, int n);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Save a dictionary to a loadable ini file
|
||||
@param d Dictionary to dump
|
||||
@param f Opened file pointer to dump to
|
||||
@return void
|
||||
|
||||
This function dumps a given dictionary into a loadable ini file.
|
||||
It is Ok to specify @c stderr or @c stdout as output files.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
void iniparser_dump_ini(dictionary * d, FILE * f);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Save a dictionary section to a loadable ini file
|
||||
@param d Dictionary to dump
|
||||
@param s Section name of dictionary to dump
|
||||
@param f Opened file pointer to dump to
|
||||
@return void
|
||||
|
||||
This function dumps a given section of a given dictionary into a loadable ini
|
||||
file. It is Ok to specify @c stderr or @c stdout as output files.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Dump a dictionary to an opened file pointer.
|
||||
@param d Dictionary to dump.
|
||||
@param f Opened file pointer to dump to.
|
||||
@return void
|
||||
|
||||
This function prints out the contents of a dictionary, one element by
|
||||
line, onto the provided file pointer. It is OK to specify @c stderr
|
||||
or @c stdout as output files. This function is meant for debugging
|
||||
purposes mostly.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void iniparser_dump(dictionary * d, FILE * f);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Get the number of keys in a section of a dictionary.
|
||||
@param d Dictionary to examine
|
||||
@param s Section name of dictionary to examine
|
||||
@return Number of keys in section
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_getsecnkeys(dictionary * d, char * s);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Get the number of keys in a section of a dictionary.
|
||||
@param d Dictionary to examine
|
||||
@param s Section name of dictionary to examine
|
||||
@return pointer to statically allocated character strings
|
||||
|
||||
This function queries a dictionary and finds all keys in a given section.
|
||||
Each pointer in the returned char pointer-to-pointer is pointing to
|
||||
a string allocated in the dictionary; do not free or modify them.
|
||||
|
||||
This function returns NULL in case of error.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
char ** iniparser_getseckeys(dictionary * d, char * s);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Get the string associated to a key
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param def Default value to return if key not found.
|
||||
@return pointer to statically allocated character string
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the pointer passed as 'def' is returned.
|
||||
The returned char pointer is pointing to a string allocated in
|
||||
the dictionary, do not free or modify it.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
char * iniparser_getstring(dictionary * d, const char * key, char * def);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Get the string associated to a key, convert to an int
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param notfound Value to return in case of error
|
||||
@return integer
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the notfound value is returned.
|
||||
|
||||
Supported values for integers include the usual C notation
|
||||
so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
|
||||
are supported. Examples:
|
||||
|
||||
- "42" -> 42
|
||||
- "042" -> 34 (octal -> decimal)
|
||||
- "0x42" -> 66 (hexa -> decimal)
|
||||
|
||||
Warning: the conversion may overflow in various ways. Conversion is
|
||||
totally outsourced to strtol(), see the associated man page for overflow
|
||||
handling.
|
||||
|
||||
Credits: Thanks to A. Becker for suggesting strtol()
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_getint(dictionary * d, const char * key, int notfound);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Get the string associated to a key, convert to a double
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param notfound Value to return in case of error
|
||||
@return double
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the notfound value is returned.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
double iniparser_getdouble(dictionary * d, const char * key, double notfound);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Get the string associated to a key, convert to a boolean
|
||||
@param d Dictionary to search
|
||||
@param key Key string to look for
|
||||
@param notfound Value to return in case of error
|
||||
@return integer
|
||||
|
||||
This function queries a dictionary for a key. A key as read from an
|
||||
ini file is given as "section:key". If the key cannot be found,
|
||||
the notfound value is returned.
|
||||
|
||||
A true boolean is found if one of the following is matched:
|
||||
|
||||
- A string starting with 'y'
|
||||
- A string starting with 'Y'
|
||||
- A string starting with 't'
|
||||
- A string starting with 'T'
|
||||
- A string starting with '1'
|
||||
|
||||
A false boolean is found if one of the following is matched:
|
||||
|
||||
- A string starting with 'n'
|
||||
- A string starting with 'N'
|
||||
- A string starting with 'f'
|
||||
- A string starting with 'F'
|
||||
- A string starting with '0'
|
||||
|
||||
The notfound value returned if no boolean is identified, does not
|
||||
necessarily have to be 0 or 1.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_getboolean(dictionary * d, const char * key, int notfound);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Set an entry in a dictionary.
|
||||
@param ini Dictionary to modify.
|
||||
@param entry Entry to modify (entry name)
|
||||
@param val New value to associate to the entry.
|
||||
@return int 0 if Ok, -1 otherwise.
|
||||
|
||||
If the given entry can be found in the dictionary, it is modified to
|
||||
contain the provided value. If it cannot be found, -1 is returned.
|
||||
It is Ok to set val to NULL.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_set(dictionary * ini, const char * entry, const char * val);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Delete an entry in a dictionary
|
||||
@param ini Dictionary to modify
|
||||
@param entry Entry to delete (entry name)
|
||||
@return void
|
||||
|
||||
If the given entry can be found, it is deleted from the dictionary.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void iniparser_unset(dictionary * ini, const char * entry);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Finds out if a given entry exists in a dictionary
|
||||
@param ini Dictionary to search
|
||||
@param entry Name of the entry to look for
|
||||
@return integer 1 if entry exists, 0 otherwise
|
||||
|
||||
Finds out if a given entry exists in the dictionary. Since sections
|
||||
are stored as keys with NULL associated values, this is the only way
|
||||
of querying for the presence of sections in a dictionary.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int iniparser_find_entry(dictionary * ini, const char * entry);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Parse an ini file and return an allocated dictionary object
|
||||
@param ininame Name of the ini file to read.
|
||||
@return Pointer to newly allocated dictionary
|
||||
|
||||
This is the parser for ini files. This function is called, providing
|
||||
the name of the file to be read. It returns a dictionary object that
|
||||
should not be accessed directly, but through accessor functions
|
||||
instead.
|
||||
|
||||
The returned dictionary must be freed using iniparser_freedict().
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
dictionary * iniparser_load(const char * ininame);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@brief Free all memory associated to an ini dictionary
|
||||
@param d Dictionary to free
|
||||
@return void
|
||||
|
||||
Free all memory associated to an ini dictionary.
|
||||
It is mandatory to call this function before the dictionary object
|
||||
gets out of the current context.
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void iniparser_freedict(dictionary * d);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "log_base.h"
|
||||
|
||||
void debug_dump_frame(char* data, int size, const char* path)
|
||||
{
|
||||
#if DUMP_STREAM_DATA
|
||||
unsigned long long ts1, ts2;
|
||||
|
||||
ts1 = debug_time_stamp_ms();
|
||||
|
||||
FILE* fp = fopen(path, "w+");
|
||||
if (fp == NULL)
|
||||
return;
|
||||
|
||||
fwrite(data, 1, size, fp);
|
||||
|
||||
fclose(fp);
|
||||
ts2 = debug_time_stamp_ms();
|
||||
|
||||
LOGD("fwrite %s bytes %d, used %lld ms\n", path, size, ts2 - ts1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void debug_dump_stream(char* data, int size, const char* path)
|
||||
{
|
||||
#if DUMP_STREAM_DATA
|
||||
unsigned long long ts1, ts2;
|
||||
|
||||
ts1 = debug_time_stamp_ms();
|
||||
|
||||
FILE* fp = fopen(path, "a+");
|
||||
if (fp == NULL)
|
||||
return;
|
||||
|
||||
fwrite(data, 1, size, fp);
|
||||
|
||||
fclose(fp);
|
||||
ts2 = debug_time_stamp_ms();
|
||||
|
||||
LOGD("fwrite %s bytes %d, used %lld ms\n", path, size, ts2 - ts1);
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned long long debug_time_stamp_ms(void)
|
||||
{
|
||||
struct timeval tv= {0};
|
||||
unsigned long long time_stamp;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
time_stamp = tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||
|
||||
return time_stamp;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __UVC_APP_DEBUG_H__
|
||||
#define __UVC_APP_DEBUG_H__
|
||||
|
||||
void debug_dump_frame(char* data, int size, const char* path);
|
||||
|
||||
void debug_dump_stream(char* data, int size, const char* path);
|
||||
|
||||
unsigned long long debug_time_stamp_ms(void);
|
||||
|
||||
#endif // __UVC_APP_DEBUG_H__
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
#ifndef __LOG_BASE_H__
|
||||
#define __LOG_BASE_H__
|
||||
|
||||
#define LOG_LEVEL_ERR 1
|
||||
#define LOG_LEVEL_WARN 2
|
||||
#define LOG_LEVEL_INFO 3
|
||||
#define LOG_LEVEL_PRINT 3
|
||||
#define LOG_LEVEL_DEBUG 5
|
||||
|
||||
/**
|
||||
* RED [error]
|
||||
* YELLOW [warn]
|
||||
* GREEN [info]
|
||||
* GREEN [trace]
|
||||
* BLUE [debug]
|
||||
*/
|
||||
|
||||
#ifdef PRINT_COLOR_ON
|
||||
#define RED "\033[1;31m"
|
||||
#define YELLOW "\033[1;33m"
|
||||
#define GREEN "\033[1;32m"
|
||||
#define BLUE "\033[1;34m"
|
||||
#define NONE "\033[m"
|
||||
#else
|
||||
#define RED ""
|
||||
#define YELLOW ""
|
||||
#define GREEN ""
|
||||
#define BLUE ""
|
||||
#define NONE ""
|
||||
#endif
|
||||
|
||||
#define TAG "LOG"
|
||||
|
||||
extern unsigned int g_loglevel;
|
||||
|
||||
#define LOGE(fmt, args...) do { \
|
||||
if (g_loglevel >= LOG_LEVEL_ERR) { \
|
||||
printf(RED "[%s E] <%s : %d> " fmt NONE, \
|
||||
TAG, __func__, __LINE__, ##args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define LOGW(fmt, args...) do { \
|
||||
if (g_loglevel >= LOG_LEVEL_WARN) { \
|
||||
printf(YELLOW "[%s W] <%s : %d> " fmt NONE, \
|
||||
TAG, __func__, __LINE__, ##args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define LOGI(fmt, args...) do { \
|
||||
if (g_loglevel >= LOG_LEVEL_INFO) { \
|
||||
printf(GREEN "[%s I] <%s : %d> " fmt NONE, \
|
||||
TAG, __func__, __LINE__, ##args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define LOGD(fmt, args...) do { \
|
||||
if (g_loglevel >= LOG_LEVEL_DEBUG) { \
|
||||
printf(NONE "[%s D] <%s : %d> " fmt NONE, \
|
||||
TAG, __func__, __LINE__, ##args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define LOGT(fmt, args...) do { \
|
||||
if (g_loglevel >= LOG_LEVEL_PRINT) { \
|
||||
printf(GREEN fmt NONE, ##args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
#ifndef __VENC_LOG_H__
|
||||
#define __VENC_LOG_H__
|
||||
|
||||
#include "log_base.h"
|
||||
|
||||
#undef TAG
|
||||
#define TAG "VENC"
|
||||
|
||||
#define venc_loge LOGE
|
||||
#define venc_logw LOGW
|
||||
#define venc_logi LOGI
|
||||
#define venc_logd LOGD
|
||||
|
||||
#define func_entry() venc_logd("entry\n")
|
||||
#define func_success() venc_logd("success\n")
|
||||
#define func_fail() venc_loge("failed\n")
|
||||
|
||||
#define check_null_goto(ptr, tag) do { \
|
||||
if ((ptr) == NULL) { \
|
||||
venc_loge("invalid param.\n"); \
|
||||
goto tag; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
#ifndef __RUN_UAC_H__
|
||||
#define __RUN_UAC_H__
|
||||
|
||||
typedef struct uac {
|
||||
int (*init)(void);
|
||||
int (*deinit)(void);
|
||||
int (*open)(void);
|
||||
int (*close)(void);
|
||||
int (*run)(void);
|
||||
int (*stop)(void);
|
||||
} uac_t;
|
||||
|
||||
#if UAC_COMPILE
|
||||
uac_t* get_uac(void);
|
||||
#else
|
||||
uac_t* get_uac(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
#ifndef __RUN_UVC_H__
|
||||
#define __RUN_UVC_H__
|
||||
|
||||
typedef struct uvc {
|
||||
int (*init)(void);
|
||||
int (*deinit)(void);
|
||||
int (*open)(void);
|
||||
int (*close)(void);
|
||||
int (*run)(void);
|
||||
int (*stop)(void);
|
||||
} uvc_t;
|
||||
|
||||
#if UVC_COMPILE
|
||||
uvc_t *get_uvc(void);
|
||||
#else
|
||||
uvc_t *get_uvc(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
;UVC Camera Application configuration file
|
||||
;it need to be located at /etc/ directory
|
||||
|
||||
[stream]
|
||||
; width-height-fps,width-height-fps
|
||||
fmt1 = yuyv,640-360-30,1280-720-13;
|
||||
fmt2 = nv21,640-360-30,1280-720-13,1920-1080-30;
|
||||
fmt3 = mjpeg,640-360-30,1280-720-30,1920-1080-30;
|
||||
fmt4 = h264,640-360-30,1280-720-30,1920-1080-30;
|
||||
;fmt5 = h265,640-360-30,1280-720-30,1920-1080-30;
|
||||
|
||||
[uvc]
|
||||
; set max PayloadImage size
|
||||
imagesize = 0x200000
|
||||
bulksize = 0x200000
|
||||
|
||||
; set cache node number
|
||||
cache_count = 3
|
||||
|
||||
; 0x1: IO_METHOD_MMAP, 0x2: IO_METHOD_USERPTR
|
||||
iomethod = 2
|
||||
|
||||
; cat /sys/kernel/config/usb_gadget/camera/functions/uvc.usb0/streaming_maxpacket
|
||||
maxpacket = 3072
|
||||
@@ -0,0 +1,39 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
|
||||
/*
|
||||
* g_uvc.h -- USB Video Class Gadget driver API
|
||||
*
|
||||
* Copyright (C) 2009-2010 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_USB_G_UVC_H
|
||||
#define __LINUX_USB_G_UVC_H
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
|
||||
#define UVC_EVENT_FIRST (V4L2_EVENT_PRIVATE_START + 0)
|
||||
#define UVC_EVENT_CONNECT (V4L2_EVENT_PRIVATE_START + 0)
|
||||
#define UVC_EVENT_DISCONNECT (V4L2_EVENT_PRIVATE_START + 1)
|
||||
#define UVC_EVENT_STREAMON (V4L2_EVENT_PRIVATE_START + 2)
|
||||
#define UVC_EVENT_STREAMOFF (V4L2_EVENT_PRIVATE_START + 3)
|
||||
#define UVC_EVENT_SETUP (V4L2_EVENT_PRIVATE_START + 4)
|
||||
#define UVC_EVENT_DATA (V4L2_EVENT_PRIVATE_START + 5)
|
||||
#define UVC_EVENT_LAST (V4L2_EVENT_PRIVATE_START + 5)
|
||||
|
||||
struct uvc_request_data {
|
||||
__s32 length;
|
||||
__u8 data[60];
|
||||
};
|
||||
|
||||
struct uvc_event {
|
||||
union {
|
||||
enum usb_device_speed speed;
|
||||
struct usb_ctrlrequest req;
|
||||
struct uvc_request_data data;
|
||||
};
|
||||
};
|
||||
|
||||
#define UVCIOC_SEND_RESPONSE _IOW('U', 1, struct uvc_request_data)
|
||||
|
||||
#endif /* __LINUX_USB_G_UVC_H */
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
#ifndef __UVC_ADAPTER_H__
|
||||
#define __UVC_ADAPTER_H__
|
||||
|
||||
/* For Requests */
|
||||
/* 和内核中的定义一致 */
|
||||
/* For VideoControl Requests */
|
||||
#define UVC_VC_INTERFACE_ID 0x00
|
||||
#define UVC_CAMERA_TERMINAL_ID 0x01
|
||||
#define UVC_PROCESSING_UNIT_ID 0x02
|
||||
#define UVC_SELECTOR_UNIT_ID 0x03
|
||||
#define UVC_OUTPUT_TERMINAL_ID 0x05
|
||||
#define UVC_EXTENSION_UNIT_H264_ID 0x0A
|
||||
#define UVC_EXTENSION_UNIT_CAMERA_ID 0x11
|
||||
#define UVC_EXTENSION_UNIT_VENDOR1_ID 0x0B
|
||||
|
||||
/* For VideoStreaming Requests*/
|
||||
#define UVC_VS_INTERFACE_ID 0x01
|
||||
|
||||
/* Requests Error Code */
|
||||
#define UVC_REC_NO_ERROR 0x00
|
||||
#define UVC_REC_NO_READY 0x01
|
||||
#define UVC_REC_WRONG_STATE 0x02
|
||||
#define UVC_REC_POWER 0x03
|
||||
#define UVC_REC_OUT_OF_RANGE 0x04
|
||||
#define UVC_REC_INVALID_UNIT 0x05
|
||||
#define UVC_REC_INVALID_CONTROL 0x06
|
||||
#define UVC_REC_INVALID_REQUEST 0x07
|
||||
#define UVC_REC_UNKNOW 0xFF
|
||||
|
||||
/* Stream Error Code */
|
||||
|
||||
/* CTRL id */
|
||||
#define XUID_SET_RESET 0x01
|
||||
#define XUID_SET_STREAM 0x02
|
||||
#define XUID_SET_RESOLUTION 0x03
|
||||
#define XUID_SET_IFRAME 0x04
|
||||
#define XUID_SET_BITRATE 0x05
|
||||
#define XUID_UPDATE_SYSTEM 0x06
|
||||
|
||||
/* VENDOR1 CTRL id */
|
||||
#define XUID_VENDOR_01 0x01
|
||||
#define XUID_VENDOR_02 0x02
|
||||
#define XUID_VENDOR_03 0x03
|
||||
#define XUID_VENDOR_04 0x04
|
||||
#define XUID_VENDOR_05 0x05
|
||||
#define XUID_VENDOR_06 0x06
|
||||
#define XUID_VENDOR_07 0x07
|
||||
#define XUID_VENDOR_08 0x08
|
||||
#define XUID_VENDOR_09 0x09
|
||||
#define XUID_VENDOR_10 0x0A
|
||||
// ...
|
||||
//#define XUID_VENDOR_32 0x20
|
||||
|
||||
/* */
|
||||
#define MASK_INTF_ID(usb_ctrl) ((usb_ctrl)->wIndex & 0xFF)
|
||||
#define MASK_ENTITY_ID(usb_ctrl) (((usb_ctrl)->wIndex & 0xFF00) >> 8)
|
||||
#define MASK_CS_CODE(usb_ctrl) ((usb_ctrl)->wValue >> 8)
|
||||
#define MASK_REQ_CODE(usb_ctrl) ((usb_ctrl)->bRequest)
|
||||
|
||||
void uvc_requests_infos(struct usb_ctrlrequest* ctrl);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,502 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "uvc_formats.h"
|
||||
#include "config_svc.h"
|
||||
#include "uvc_log.h"
|
||||
|
||||
#define FORMAT_NV12 "nv12"
|
||||
#define FORMAT_NV21 "nv21"
|
||||
#define FORMAT_YUYV "yuyv"
|
||||
#define FORMAT_H264 "h264"
|
||||
#define FORMAT_H265 "h265"
|
||||
#define FORMAT_MJPEG "mjpeg"
|
||||
|
||||
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
|
||||
#define v4l2_fourcc(a, b, c, d)\
|
||||
((__u32)(a) | ((__u32)(b) << 8) | ((__u32)(c) << 16) | ((__u32)(d) << 24))
|
||||
|
||||
#define V4L2_PIX_FMT_H265 v4l2_fourcc('H', '2', '6', '5') /* H265 with start codes */
|
||||
|
||||
|
||||
struct node {
|
||||
void* data;
|
||||
struct node* row;
|
||||
struct node* column;
|
||||
};
|
||||
|
||||
static struct node g_formats_list_head = {
|
||||
.data = NULL,
|
||||
.row = NULL,
|
||||
.column = NULL,
|
||||
};
|
||||
|
||||
static int g_malloc_count = 0;
|
||||
|
||||
static char* fcc_to_string(int fcc)
|
||||
{
|
||||
switch (fcc) {
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
return "yuyv";
|
||||
|
||||
case V4L2_PIX_FMT_NV21:
|
||||
return "nv21";
|
||||
|
||||
case V4L2_PIX_FMT_NV12:
|
||||
return "nv12";
|
||||
|
||||
case V4L2_PIX_FMT_MJPEG:
|
||||
return "mjpeg";
|
||||
|
||||
case V4L2_PIX_FMT_H264:
|
||||
return "h264";
|
||||
|
||||
case V4L2_PIX_FMT_H265:
|
||||
return "h265";
|
||||
|
||||
default:
|
||||
return "Unknow";
|
||||
}
|
||||
}
|
||||
|
||||
static int string_to_fcc(char* str)
|
||||
{
|
||||
if (memcmp(str, FORMAT_YUYV, strlen(str)) == 0) {
|
||||
return V4L2_PIX_FMT_YUYV;
|
||||
}
|
||||
|
||||
if (memcmp(str, FORMAT_NV21, strlen(str)) == 0) {
|
||||
return V4L2_PIX_FMT_NV21;
|
||||
}
|
||||
|
||||
if (memcmp(str, FORMAT_NV12, strlen(str)) == 0) {
|
||||
return V4L2_PIX_FMT_NV12;
|
||||
}
|
||||
|
||||
if (memcmp(str, FORMAT_MJPEG, strlen(str)) == 0) {
|
||||
return V4L2_PIX_FMT_MJPEG;
|
||||
}
|
||||
|
||||
if (memcmp(str, FORMAT_H264, strlen(str)) == 0) {
|
||||
return V4L2_PIX_FMT_H264;
|
||||
}
|
||||
|
||||
if (memcmp(str, FORMAT_H265, strlen(str)) == 0) {
|
||||
return V4L2_PIX_FMT_H265;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void show_formats_info(void)
|
||||
{
|
||||
int i, j;
|
||||
struct node* list_head = &g_formats_list_head;
|
||||
struct node* fmt_n = NULL;
|
||||
struct node* frm_n = NULL;
|
||||
struct uvc_format_info* fmt_info = NULL;
|
||||
struct uvc_frame_info* frm_info = NULL;
|
||||
|
||||
printf("\n");
|
||||
printf("Formats info:\n");
|
||||
fmt_n = list_head->row;
|
||||
i = 1;
|
||||
while (fmt_n) {
|
||||
fmt_info = (struct uvc_format_info*)fmt_n->data;
|
||||
printf("\tFormat %d: %s\n", i, fcc_to_string(fmt_info->fcc));
|
||||
frm_n = fmt_n->column;
|
||||
j = 1;
|
||||
while (frm_n) {
|
||||
frm_info = (struct uvc_frame_info*)frm_n->data;
|
||||
printf("\tFrame %d: \t%d x %d \t@ %d.\n",
|
||||
j, frm_info->width, frm_info->height, frm_info->intervals[0]);
|
||||
frm_n = frm_n->column;
|
||||
j++;
|
||||
}
|
||||
|
||||
fmt_n = fmt_n->row;
|
||||
i++;
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static struct node* new_node(void* data)
|
||||
{
|
||||
struct node* new = malloc(sizeof(struct node));
|
||||
|
||||
if (new == NULL) {
|
||||
uvc_loge("Out of memory\n");
|
||||
} else {
|
||||
g_malloc_count++;
|
||||
new->data = data;
|
||||
new->row = NULL;
|
||||
new->column = NULL;
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
static struct node* new_format(int fcc)
|
||||
{
|
||||
struct node* new_fmt = NULL;
|
||||
struct uvc_format_info* fmt = malloc(sizeof(struct uvc_format_info));
|
||||
|
||||
if (fmt == NULL) {
|
||||
uvc_loge("Out of memory.\n");
|
||||
} else {
|
||||
g_malloc_count++;
|
||||
fmt->fcc = fcc;
|
||||
fmt->frames = NULL;
|
||||
fmt->frm_nums = 0;
|
||||
new_fmt = new_node(fmt);
|
||||
if (new_fmt == NULL) {
|
||||
free(fmt);
|
||||
g_malloc_count--;
|
||||
fmt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return new_fmt;
|
||||
}
|
||||
|
||||
static struct node* new_frame(int w, int h, int interval)
|
||||
{
|
||||
struct node* new_frm = NULL;
|
||||
struct uvc_frame_info* frm = malloc(sizeof(struct uvc_frame_info));
|
||||
|
||||
if (frm == NULL) {
|
||||
uvc_loge("Out of memory.\n");
|
||||
} else {
|
||||
g_malloc_count++;
|
||||
memset(frm, 0, sizeof(struct uvc_frame_info));
|
||||
frm->width = w;
|
||||
frm->height = h;
|
||||
frm->intervals[0] = interval;
|
||||
|
||||
new_frm = new_node(frm);
|
||||
if (new_frm == NULL) {
|
||||
free(frm);
|
||||
g_malloc_count--;
|
||||
frm = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return new_frm;
|
||||
}
|
||||
|
||||
static void add_format_tail(struct node* list_head, struct node* new_fmt)
|
||||
{
|
||||
struct node* cur = list_head;
|
||||
|
||||
while (cur->row) {
|
||||
cur = cur->row;
|
||||
}
|
||||
|
||||
cur->row = new_fmt;
|
||||
}
|
||||
|
||||
static void add_frame_tail(struct node* list_head, int fcc, struct node* new_frm)
|
||||
{
|
||||
struct node* frm = NULL;
|
||||
struct node* fmt = list_head->row;
|
||||
struct uvc_format_info* fmt_info = NULL;
|
||||
|
||||
while (fmt) {
|
||||
fmt_info = (struct uvc_format_info*)fmt->data;
|
||||
if (fmt_info->fcc == fcc) {
|
||||
fmt_info->frm_nums++;
|
||||
break;
|
||||
} else {
|
||||
fmt = fmt->row;
|
||||
}
|
||||
}
|
||||
|
||||
if (fmt == NULL) {
|
||||
uvc_loge("Not found format: %d.\n", fcc);
|
||||
return;
|
||||
}
|
||||
|
||||
frm = fmt;
|
||||
while (frm->column) {
|
||||
frm = frm->column;
|
||||
}
|
||||
|
||||
frm->column = new_frm;
|
||||
}
|
||||
|
||||
static void add_format(struct node* list_head, int fcc)
|
||||
{
|
||||
struct node* new_fmt = new_format(fcc);
|
||||
|
||||
add_format_tail(list_head, new_fmt);
|
||||
}
|
||||
|
||||
static void add_frame(struct node* list_head, int fcc, struct uvc_frame_info frm)
|
||||
{
|
||||
struct node* new_frm = new_frame(frm.width, frm.height, frm.intervals[0]);
|
||||
|
||||
add_frame_tail(list_head, fcc, new_frm);
|
||||
}
|
||||
|
||||
static char* parser_sub(char* in, char* out, char k)
|
||||
{
|
||||
char* tmp = in;
|
||||
|
||||
while (1) {
|
||||
if (*tmp == '\0') {
|
||||
tmp = NULL;
|
||||
break;
|
||||
} else if (*tmp == k) {
|
||||
/* step k */
|
||||
tmp++;
|
||||
break;
|
||||
} else {
|
||||
*out++ = *tmp++;
|
||||
}
|
||||
}
|
||||
|
||||
*out = '\0';
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static char* parser_format(char* str, char* format)
|
||||
{
|
||||
char k = ',';
|
||||
return parser_sub(str, format, k);
|
||||
}
|
||||
|
||||
static char* parser_frame(char* formats, char* frame)
|
||||
{
|
||||
char k = ',';
|
||||
return parser_sub(formats, frame, k);
|
||||
}
|
||||
|
||||
static int frame_interval(int fps)
|
||||
{
|
||||
return ((1000 * 10000) / fps);
|
||||
}
|
||||
|
||||
static void parser_whf(char* frame, struct uvc_frame_info* frm_info)
|
||||
{
|
||||
int i;
|
||||
char param[16];
|
||||
char* tmp = frame;
|
||||
char k = '-';
|
||||
|
||||
/* width */
|
||||
bzero(param, sizeof(param));
|
||||
tmp = parser_sub(tmp, param, k);
|
||||
frm_info->width = atoi(param);
|
||||
|
||||
/* height */
|
||||
bzero(param, sizeof(param));
|
||||
tmp = parser_sub(tmp, param, k);
|
||||
frm_info->height = atoi(param);
|
||||
|
||||
/* fps */
|
||||
i = 0;
|
||||
do {
|
||||
bzero(param, sizeof(param));
|
||||
tmp = parser_sub(tmp, param, k);
|
||||
|
||||
if (i == ARRAY_SIZE(frm_info->intervals)) {
|
||||
break;
|
||||
}
|
||||
|
||||
frm_info->intervals[i++] = frame_interval(atoi(param));
|
||||
} while (tmp != NULL);
|
||||
}
|
||||
|
||||
static int init_format(char* key)
|
||||
{
|
||||
int rc, fcc;
|
||||
char str[256];
|
||||
char frame[256];
|
||||
char format[16];
|
||||
char stream[32] = "stream:";
|
||||
char* tmp = NULL;
|
||||
struct uvc_frame_info frm_info= {0};
|
||||
|
||||
strcat(stream, key);
|
||||
bzero(str, sizeof(str));
|
||||
rc = get_config_string(stream, str, sizeof(str));
|
||||
if (rc < 0) {
|
||||
uvc_logd("Not found %s\n", stream);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tmp = (char*)str;
|
||||
|
||||
bzero(format, sizeof(format));
|
||||
tmp = parser_format(tmp, format);
|
||||
if (tmp == NULL) {
|
||||
uvc_loge("parser_format fail\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uvc_logd("Format %s: %s\n", format, tmp);
|
||||
|
||||
fcc = string_to_fcc(format);
|
||||
add_format(&g_formats_list_head, fcc);
|
||||
do {
|
||||
bzero(frame, sizeof(frame));
|
||||
tmp = parser_frame(tmp, frame);
|
||||
|
||||
parser_whf(frame, &frm_info);
|
||||
|
||||
add_frame(&g_formats_list_head, fcc, frm_info);
|
||||
} while (tmp != NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int frame_min(int* fmt_index, int* frm_index)
|
||||
{
|
||||
*fmt_index = 1;
|
||||
*frm_index = 1;
|
||||
|
||||
uvc_logd("Format min {format %d, frame %d}\n", *fmt_index, *frm_index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int frame_max(int* fmt_index, int* frm_index)
|
||||
{
|
||||
struct node* list_head = &g_formats_list_head;
|
||||
struct node* tmp = list_head;
|
||||
int index;
|
||||
|
||||
index = 0;
|
||||
while (tmp->row) {
|
||||
tmp = tmp->row;
|
||||
index++;
|
||||
}
|
||||
*fmt_index = index;
|
||||
|
||||
index = 0;
|
||||
while (tmp->column) {
|
||||
tmp = tmp->column;
|
||||
index++;
|
||||
}
|
||||
*frm_index = index;
|
||||
|
||||
uvc_logd("Format max {format %d, frame %d}\n", *fmt_index, *frm_index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int find_frame(int* fmt_index, int* frm_index, int* fcc, struct uvc_frame_info* frame)
|
||||
{
|
||||
struct node* list_head = &g_formats_list_head;
|
||||
struct node* tmp = list_head;
|
||||
struct uvc_format_info* fmt = NULL;
|
||||
struct uvc_frame_info* frm = NULL;
|
||||
int index;
|
||||
|
||||
if (list_head->row == NULL) {
|
||||
uvc_loge("UVC formats is not initialized.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*fmt_index == 0) {
|
||||
frame_min(fmt_index, frm_index);
|
||||
} else if (*fmt_index < 0) {
|
||||
frame_max(fmt_index, frm_index);
|
||||
}
|
||||
|
||||
if (frame == NULL || fcc == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find format */
|
||||
index = *fmt_index;
|
||||
while (index-- && tmp) {
|
||||
tmp = tmp->row;
|
||||
}
|
||||
|
||||
if (tmp == NULL) {
|
||||
uvc_logw("Not found {format %d , frame %d }, Use {format 1, frame 1}\n", *fmt_index, *frm_index);
|
||||
show_formats_info();
|
||||
fflush(stdout);
|
||||
|
||||
*fmt_index = 1;
|
||||
*frm_index = 1;
|
||||
|
||||
return find_frame(fmt_index, frm_index, fcc, frame);
|
||||
}
|
||||
|
||||
fmt = (struct uvc_format_info*)tmp->data;
|
||||
*fcc = fmt->fcc;
|
||||
|
||||
/* Find frame */
|
||||
index = *frm_index;
|
||||
while (index-- && tmp) {
|
||||
tmp = tmp->column;
|
||||
}
|
||||
|
||||
if (tmp == NULL) {
|
||||
uvc_logw("Not found {format %d, frame %d}, Use {format %d, frame 1}\n", *fmt_index, *frm_index, *fmt_index);
|
||||
show_formats_info();
|
||||
fflush(stdout);
|
||||
|
||||
*frm_index = 1;
|
||||
|
||||
return find_frame(fmt_index, frm_index, fcc, frame);
|
||||
}
|
||||
|
||||
frm = (struct uvc_frame_info*)tmp->data;
|
||||
memcpy(frame, frm, sizeof(struct uvc_frame_info));
|
||||
|
||||
uvc_logd("Find {format %d, frame %d} : %s, %dx%d @ %d\n", *fmt_index, *frm_index,
|
||||
fcc_to_string(*fcc), frame->width, frame->height, frame->intervals[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void uvc_formats_init(void)
|
||||
{
|
||||
int i, ret;
|
||||
char key[16];
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
bzero(key, sizeof(key));
|
||||
i++;
|
||||
snprintf(key, sizeof(key), "fmt%d", i);
|
||||
ret = init_format(key);
|
||||
} while (ret == 0);
|
||||
}
|
||||
|
||||
void uvc_formats_deinit(void)
|
||||
{
|
||||
struct node* list_head = &g_formats_list_head;
|
||||
struct node* row = list_head->row;
|
||||
struct node* tmp = NULL;
|
||||
struct node* column = NULL;
|
||||
|
||||
while (row) {
|
||||
list_head->row = row->row;
|
||||
|
||||
/* release resources */
|
||||
column = row;
|
||||
while (column) {
|
||||
tmp = column->column;
|
||||
free(column->data);
|
||||
g_malloc_count--;
|
||||
free(column);
|
||||
g_malloc_count--;
|
||||
column = tmp;
|
||||
}
|
||||
|
||||
row = list_head->row;
|
||||
}
|
||||
|
||||
if (g_malloc_count) {
|
||||
uvc_logw("Resource leakage.\n");
|
||||
} else {
|
||||
uvc_logi("Release resources ok.\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
/*
|
||||
* uvc formats
|
||||
*/
|
||||
|
||||
#ifndef __UVC_FORMATS_H__
|
||||
#define __UVC_FORMATS_H__
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#if 1
|
||||
struct uvc_frame_info {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int intervals[8];
|
||||
};
|
||||
|
||||
struct uvc_format_info {
|
||||
unsigned int fcc;
|
||||
unsigned int frm_nums;
|
||||
const struct uvc_frame_info* frames;
|
||||
};
|
||||
|
||||
void uvc_formats_init(void);
|
||||
|
||||
void uvc_formats_deinit(void);
|
||||
|
||||
int find_frame(int* fmt_index, int* frm_index, int* fcc, struct uvc_frame_info* frame);
|
||||
|
||||
#else
|
||||
struct uvc_frame_info {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int intervals[8];
|
||||
};
|
||||
|
||||
struct uvc_format_info {
|
||||
unsigned int fcc;
|
||||
const struct uvc_frame_info* frames;
|
||||
};
|
||||
|
||||
static const struct uvc_frame_info uvc_frames_yuv[] = {
|
||||
{ 640, 360, {333333, 0 }, },
|
||||
{ 1280, 720, {333333, 0 }, },
|
||||
{ 1920, 1080, {333333, 0 }, },
|
||||
{ 3840, 2160, {333333, 0 }, },
|
||||
{ 0, 0, { 0, }, },
|
||||
};
|
||||
|
||||
static const struct uvc_frame_info uvc_frames_mjpeg[] = {
|
||||
{ 640, 360, {333333, 0 }, },
|
||||
{ 1280, 720, {333333, 0 }, },
|
||||
{ 1920, 1080, {333333, 0 }, },
|
||||
{ 3840, 2160, {333333, 0 }, },
|
||||
{ 0, 0, { 0, }, },
|
||||
};
|
||||
|
||||
static const struct uvc_frame_info uvc_frames_h264[] = {
|
||||
{ 640, 360, {333333, 0 }, },
|
||||
{ 1280, 720, {333333, 0 }, },
|
||||
{ 1920, 1080, {333333, 0 }, },
|
||||
{ 3840, 2160, {333333, 0 }, },
|
||||
{ 0, 0, { 0, }, },
|
||||
};
|
||||
|
||||
static const struct uvc_format_info uvc_formats[] = {
|
||||
{V4L2_PIX_FMT_YUYV, uvc_frames_yuv},
|
||||
{V4L2_PIX_FMT_MJPEG, uvc_frames_mjpeg},
|
||||
{V4L2_PIX_FMT_H264, uvc_frames_h264},
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __UVC_HAL_H__
|
||||
#define __UVC_HAL_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <linux/usb/g_uvc.h>
|
||||
#include <linux/usb/video.h>
|
||||
|
||||
typedef struct uvc_probe_t {
|
||||
unsigned char set;
|
||||
unsigned char get;
|
||||
unsigned char max;
|
||||
unsigned char min;
|
||||
} uvc_probe_t;
|
||||
|
||||
struct uvc_device {
|
||||
int fd;
|
||||
int streaming;
|
||||
|
||||
unsigned int req;
|
||||
unsigned int control;
|
||||
unsigned int unit_id;
|
||||
unsigned int interface_id;
|
||||
|
||||
unsigned int fcc;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int fps;
|
||||
unsigned int bulk;
|
||||
unsigned int bulk_size;
|
||||
unsigned int nbufs;
|
||||
unsigned int imgsize;
|
||||
unsigned int max_payload_size;
|
||||
unsigned char color;
|
||||
|
||||
/* USB speed specific */
|
||||
int mult;
|
||||
int burst;
|
||||
int maxpkt;
|
||||
enum usb_device_speed speed;
|
||||
|
||||
uvc_probe_t probe_status;
|
||||
struct uvc_streaming_control probe;
|
||||
struct uvc_streaming_control commit;
|
||||
|
||||
struct uvc_request_data request_error_code;
|
||||
};
|
||||
|
||||
int open_uvc_device();
|
||||
int close_uvc_device();
|
||||
int run_uvc_device();
|
||||
int run_uvc_data();
|
||||
|
||||
#endif //__UVC_HAL_H__
|
||||
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/video.h>
|
||||
|
||||
#include "uvc_log.h"
|
||||
#include "uvc_adapter.h"
|
||||
|
||||
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
* +++++++++++++++++++ Requests infos +++++++++++++++++++++++++++
|
||||
*/
|
||||
|
||||
/* 4.2.2.1 Camera Terminal Control Requests */
|
||||
static char* vc_camera_terminal_cs(unsigned char cs_code)
|
||||
{
|
||||
switch (cs_code) {
|
||||
case UVC_CT_SCANNING_MODE_CONTROL:
|
||||
return "Sanning Mode";
|
||||
case UVC_CT_AE_MODE_CONTROL:
|
||||
return "Auto-Exposure Mode";
|
||||
case UVC_CT_AE_PRIORITY_CONTROL:
|
||||
return "Auto-Exposure Priority";
|
||||
case UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL:
|
||||
return "Exposure Time(Absolute)";
|
||||
case UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL:
|
||||
return "Exposure Time(Relative)";
|
||||
case UVC_CT_FOCUS_ABSOLUTE_CONTROL:
|
||||
return "Focus(Absolute)";
|
||||
case UVC_CT_FOCUS_RELATIVE_CONTROL:
|
||||
return "Focus(Relative)";
|
||||
case UVC_CT_FOCUS_AUTO_CONTROL:
|
||||
return "Focus(Auto)";
|
||||
case UVC_CT_IRIS_ABSOLUTE_CONTROL:
|
||||
return "Iris(Absolute)";
|
||||
case UVC_CT_IRIS_RELATIVE_CONTROL:
|
||||
return "Iris(Relative)";
|
||||
case UVC_CT_ZOOM_ABSOLUTE_CONTROL:
|
||||
return "Zoom(Absolute)";
|
||||
case UVC_CT_ZOOM_RELATIVE_CONTROL:
|
||||
return "Zoom(Relative)";
|
||||
case UVC_CT_PANTILT_ABSOLUTE_CONTROL:
|
||||
return "PanTilt(Absolute)";
|
||||
case UVC_CT_PANTILT_RELATIVE_CONTROL:
|
||||
return "PanTilt(Relative)";
|
||||
case UVC_CT_ROLL_ABSOLUTE_CONTROL:
|
||||
return "Roll(Absolute)";
|
||||
case UVC_CT_ROLL_RELATIVE_CONTROL:
|
||||
return "Roll(Relative)";
|
||||
case UVC_CT_PRIVACY_CONTROL:
|
||||
return "Privacy Shutter";
|
||||
case UVC_CT_CONTROL_UNDEFINED:
|
||||
default:
|
||||
return "Invalid control";
|
||||
}
|
||||
}
|
||||
|
||||
/* 4.2.2.2 Selector Unit Control Requests */
|
||||
static char* vc_selector_unit_cs(unsigned char cs_code)
|
||||
{
|
||||
switch (cs_code) {
|
||||
case UVC_SU_INPUT_SELECT_CONTROL:
|
||||
return "Selector Unit";
|
||||
case UVC_SU_CONTROL_UNDEFINED:
|
||||
default:
|
||||
return "Invalid control";
|
||||
}
|
||||
}
|
||||
|
||||
/* 4.2.2.3 Processing Unit Control Requests */
|
||||
static char* vc_processing_unit_cs(unsigned char cs_code)
|
||||
{
|
||||
switch (cs_code) {
|
||||
case UVC_PU_BACKLIGHT_COMPENSATION_CONTROL:
|
||||
return "Backlight compensation";
|
||||
case UVC_PU_BRIGHTNESS_CONTROL:
|
||||
return "Brightness";
|
||||
case UVC_PU_CONTRAST_CONTROL:
|
||||
return "Contrast";
|
||||
case UVC_PU_GAIN_CONTROL:
|
||||
return "Gain";
|
||||
case UVC_PU_POWER_LINE_FREQUENCY_CONTROL:
|
||||
return "Power Line Frequency";
|
||||
case UVC_PU_HUE_CONTROL:
|
||||
return "Hue";
|
||||
case UVC_PU_HUE_AUTO_CONTROL:
|
||||
return "Hue,Auto";
|
||||
case UVC_PU_SATURATION_CONTROL:
|
||||
return "Saturation";
|
||||
case UVC_PU_SHARPNESS_CONTROL:
|
||||
return "Sharpness";
|
||||
case UVC_PU_GAMMA_CONTROL:
|
||||
return "Gamma";
|
||||
case UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL:
|
||||
return "White Balance Temperature";
|
||||
case UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL:
|
||||
return "White Balance Temperature,Auto";
|
||||
case UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL:
|
||||
return "White Balance Component";
|
||||
case UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL:
|
||||
return "White Balance Component,Auto";
|
||||
case UVC_PU_DIGITAL_MULTIPLIER_CONTROL:
|
||||
return "Digital Multiplier";
|
||||
case UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL:
|
||||
return "Digital Multiplier Limit";
|
||||
case UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL:
|
||||
return "Analog Video Standard";
|
||||
case UVC_PU_ANALOG_LOCK_STATUS_CONTROL:
|
||||
return "Analog Video Lock Status";
|
||||
case UVC_PU_CONTROL_UNDEFINED:
|
||||
default:
|
||||
return "Invalid control";
|
||||
}
|
||||
}
|
||||
|
||||
/* 4.2.2.4 Extension Unit Control Requests */
|
||||
static char* vc_extension_unit_h264_cs(unsigned char cs_code)
|
||||
{
|
||||
return "Invalid control";
|
||||
}
|
||||
|
||||
/* 4.2.1 Intercace Control Requests */
|
||||
static char* vc_interface_cs(unsigned char cs_code)
|
||||
{
|
||||
switch (cs_code) {
|
||||
case UVC_VC_VIDEO_POWER_MODE_CONTROL:
|
||||
return "Device Power Mode";
|
||||
case UVC_VC_REQUEST_ERROR_CODE_CONTROL:
|
||||
return "Request Error Code Control";
|
||||
case UVC_VC_CONTROL_UNDEFINED:
|
||||
default:
|
||||
return "Invalid control";
|
||||
}
|
||||
}
|
||||
|
||||
/* 4.3.1 Interface Control Requests */
|
||||
static char* vs_interface_cs(unsigned char cs_code)
|
||||
{
|
||||
switch (cs_code) {
|
||||
case UVC_VS_PROBE_CONTROL:
|
||||
return "Video Probe";
|
||||
case UVC_VS_COMMIT_CONTROL:
|
||||
return "Video Commit";
|
||||
case UVC_VS_STILL_PROBE_CONTROL:
|
||||
return "Video Still Probe";
|
||||
case UVC_VS_STILL_COMMIT_CONTROL:
|
||||
return "Video Still Commit";
|
||||
case UVC_VS_STILL_IMAGE_TRIGGER_CONTROL:
|
||||
return "Still Image Trigger";
|
||||
case UVC_VS_STREAM_ERROR_CODE_CONTROL:
|
||||
return "Stream Error Code";
|
||||
case UVC_VS_GENERATE_KEY_FRAME_CONTROL:
|
||||
return "Generate Key Frame";
|
||||
case UVC_VS_UPDATE_FRAME_SEGMENT_CONTROL:
|
||||
return "Update Frame Segment";
|
||||
case UVC_VS_SYNC_DELAY_CONTROL:
|
||||
return "Sync Delay";
|
||||
case UVC_VS_CONTROL_UNDEFINED:
|
||||
default:
|
||||
return "Invalid control";
|
||||
}
|
||||
}
|
||||
|
||||
/* 4.2.2 Unit and Terminal Control Requests */
|
||||
static char* vc_entity_name(unsigned char entity_id)
|
||||
{
|
||||
switch (entity_id) {
|
||||
case UVC_CAMERA_TERMINAL_ID:
|
||||
return "Camera Terminal";
|
||||
case UVC_PROCESSING_UNIT_ID:
|
||||
return "Processing Unit";
|
||||
case UVC_OUTPUT_TERMINAL_ID:
|
||||
return "Output Terminal";
|
||||
case UVC_SELECTOR_UNIT_ID:
|
||||
return "Selector Unit";
|
||||
case UVC_EXTENSION_UNIT_H264_ID:
|
||||
return "Extension Unit H264";
|
||||
default:
|
||||
return "Invalid Unit";
|
||||
}
|
||||
}
|
||||
|
||||
static char* control_entity_cs(unsigned char entity_id, unsigned char cs_code)
|
||||
{
|
||||
switch (entity_id) {
|
||||
case UVC_CAMERA_TERMINAL_ID:
|
||||
return vc_camera_terminal_cs(cs_code);
|
||||
case UVC_PROCESSING_UNIT_ID:
|
||||
return vc_processing_unit_cs(cs_code);
|
||||
case UVC_OUTPUT_TERMINAL_ID:
|
||||
return "Unsupported unit";
|
||||
case UVC_SELECTOR_UNIT_ID:
|
||||
return vc_selector_unit_cs(cs_code);
|
||||
case UVC_EXTENSION_UNIT_H264_ID:
|
||||
return vc_extension_unit_h264_cs(cs_code);
|
||||
default:
|
||||
return "Invalid unit";
|
||||
}
|
||||
}
|
||||
|
||||
/* 4.2.1 Intercace Control Requests */
|
||||
/* 4.3.1 Interface Control Requests */
|
||||
static char* control_interface_name(unsigned char interface_id)
|
||||
{
|
||||
switch (interface_id) {
|
||||
case UVC_VS_INTERFACE_ID:
|
||||
return "VS Interface";
|
||||
case UVC_VC_INTERFACE_ID:
|
||||
return "VC Interface";
|
||||
default:
|
||||
return "Invalid Interface";
|
||||
}
|
||||
}
|
||||
|
||||
static char* control_interface_cs(unsigned char interface_id, unsigned char cs_code)
|
||||
{
|
||||
switch (interface_id) {
|
||||
case UVC_VC_INTERFACE_ID:
|
||||
return vc_interface_cs(cs_code);
|
||||
case UVC_VS_INTERFACE_ID:
|
||||
return vs_interface_cs(cs_code);
|
||||
default:
|
||||
return "Invalid Interface";
|
||||
}
|
||||
}
|
||||
|
||||
/* 4.1 Requests Layout */
|
||||
static char* request_name(unsigned char req_id)
|
||||
{
|
||||
switch (req_id) {
|
||||
case UVC_SET_CUR:
|
||||
return "Set Cur";
|
||||
case UVC_GET_CUR:
|
||||
return "Get Cur";
|
||||
case UVC_GET_MIN:
|
||||
return "Get Min";
|
||||
case UVC_GET_MAX:
|
||||
return "Get Max";
|
||||
case UVC_GET_RES:
|
||||
return "Get Res";
|
||||
case UVC_GET_LEN:
|
||||
return "Get Len";
|
||||
case UVC_GET_INFO:
|
||||
return "Get Info";
|
||||
case UVC_GET_DEF:
|
||||
return "Get Def";
|
||||
case UVC_RC_UNDEFINED:
|
||||
default:
|
||||
return "Invalid request";
|
||||
}
|
||||
}
|
||||
|
||||
/* +++++++++++++++++++ Requests infos +++++++++++++++++++++++++++
|
||||
* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
*/
|
||||
|
||||
/* Requests Debug */
|
||||
/*
|
||||
* Node: f_uvc.c 中定义 entity_id,各entity的ID不会为0;
|
||||
* f_uvc.c 中定义 video control interface 为 0;
|
||||
* f_uvc.c 中定义 video streaming interface 为1;
|
||||
*/
|
||||
void uvc_requests_infos(struct usb_ctrlrequest* ctrl)
|
||||
{
|
||||
// unit_id != 0, video Control
|
||||
unsigned char entity_id = MASK_ENTITY_ID(ctrl);
|
||||
|
||||
// interface_id == 0 : video control interface
|
||||
// interface_id == 1 : video streaming interface
|
||||
unsigned char interface_id = MASK_INTF_ID(ctrl);
|
||||
|
||||
unsigned char cs_code = MASK_CS_CODE(ctrl);
|
||||
unsigned char req_id = MASK_REQ_CODE(ctrl);
|
||||
|
||||
uvc_logd("reqeust type : INTERFACE.\n");
|
||||
uvc_logd("class-specific : 0x%02x(%s).\n",
|
||||
interface_id, interface_id ? "Video Streaming" : "Video Control");
|
||||
if (entity_id) {
|
||||
uvc_logd("entity id : 0x%02x(%s)\n", entity_id, vc_entity_name(entity_id));
|
||||
uvc_logd("control selector : 0x%02x(%s)\n", cs_code, control_entity_cs(entity_id, cs_code));
|
||||
} else {
|
||||
uvc_logd("interface : 0x%02x(%s)\n", interface_id, control_interface_name(interface_id));
|
||||
uvc_logd("control selector : 0x%02x(%s)\n", cs_code, control_interface_cs(interface_id, cs_code));
|
||||
}
|
||||
uvc_logd("requests : 0x%02x(%s)\n",req_id, request_name(req_id));
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MARK_SOI 0xFFD8
|
||||
#define MARK_APP0 0xFFE0
|
||||
#define MARK_DQT 0xFFDB
|
||||
#define MARK_SOF0 0xFFC0
|
||||
#define MARK_DHT 0xFFC4
|
||||
#define MARK_SOS 0xFFDA
|
||||
#define MARK_DRI 0xFFDD
|
||||
#define MARK_DOI 0xFFD9
|
||||
|
||||
#define SPLIT_DHT 1
|
||||
/* 7205 7606, There are 3 quantitative tables (DQT0 DQT1 DQT2)
|
||||
* 7203 7206, There are 2 quantitative tables (DQT0 DQT1) */
|
||||
#define REMOVE_DQT2 0
|
||||
#define REMOVE_DRI 1
|
||||
|
||||
/*
|
||||
* Delete DRI field in JPEG, delete quantization table 2 (DQT2), split Huffman table.
|
||||
*/
|
||||
void memcpy_jpeg_user(unsigned char* src_buffer, unsigned int src_len,
|
||||
unsigned char* dest_buffer, unsigned int dest_length, unsigned int* dest_offset)
|
||||
{
|
||||
unsigned int mark, sec_len;
|
||||
unsigned int copy_size;
|
||||
unsigned int src_off = 0;
|
||||
unsigned int dst_len = dest_length;
|
||||
unsigned int dst_off = *dest_offset;
|
||||
unsigned char* src_buf = src_buffer;
|
||||
unsigned char* dst_buf = (dest_buffer + dst_off);
|
||||
|
||||
while ((src_len - src_off) > 0) {
|
||||
mark = ((src_buf[0] << 8) | src_buf[1]);
|
||||
sec_len = ((src_buf[2] << 8) | src_buf[3]);
|
||||
|
||||
switch (mark) {
|
||||
case MARK_SOI:
|
||||
//printf("MARK_SOI\n");
|
||||
/* copy SOI */
|
||||
copy_size = 2;
|
||||
copy_size = ((dst_len - dst_off) > copy_size) ? copy_size : (dst_len - dst_off);
|
||||
memcpy(dst_buf, src_buf, copy_size);
|
||||
src_buf += 2;
|
||||
src_off += 2;
|
||||
dst_buf += copy_size;
|
||||
dst_off += copy_size;
|
||||
break;
|
||||
|
||||
case MARK_APP0:
|
||||
/* copy APP0 */
|
||||
//printf("MARK_APP0\n");
|
||||
copy_size = (2 + sec_len);
|
||||
copy_size = ((dst_len - dst_off) > copy_size) ? copy_size : (dst_len - dst_off);
|
||||
memcpy(dst_buf, src_buf, copy_size);
|
||||
src_buf += (2 + sec_len);
|
||||
src_off += (2 + sec_len);
|
||||
dst_buf += copy_size;
|
||||
dst_off += copy_size;
|
||||
break;
|
||||
|
||||
case MARK_DQT:
|
||||
//printf("MARK_DQT\n");
|
||||
copy_size = (2 + sec_len);
|
||||
#if REMOVE_DQT2
|
||||
/* Delete the third quantification table */
|
||||
/* After JPEG encoding on the Xmdeia platform, there are 3 quantization tables,
|
||||
* while some decoding platforms only support 2 quantization tables.*/
|
||||
if ((dst_len - dst_off) > copy_size) {
|
||||
/* copy DQT0 and refactoring DQT */
|
||||
dst_buf[0] = 0xFF;
|
||||
dst_buf[1] = 0xDB;
|
||||
dst_buf[2] = 0x00;
|
||||
dst_buf[3] = 0x43;
|
||||
dst_buf[4] = 0x00; /* DQT0 */
|
||||
dst_buf += 5;
|
||||
dst_off += 5;
|
||||
src_buf += 5;
|
||||
memcpy(dst_buf, src_buf, 0x40); /* 0x40: DQT0 size */
|
||||
dst_buf += 0x40;
|
||||
dst_off += 0x40;
|
||||
src_buf += 0x40;
|
||||
|
||||
/* copy DQT1 and refactoring DQT*/
|
||||
dst_buf[0] = 0xFF;
|
||||
dst_buf[1] = 0xDB;
|
||||
dst_buf[2] = 0x00;
|
||||
dst_buf[3] = 0x43;
|
||||
dst_buf[4] = 0x01; /* DQT1 */
|
||||
dst_buf += 5;
|
||||
dst_off += 5;
|
||||
src_buf += 1;
|
||||
memcpy(dst_buf, src_buf, 0x40); /* 0x40: DQT1 size */
|
||||
dst_buf += 0x40;
|
||||
dst_off += 0x40;
|
||||
src_buf += 0x40;
|
||||
#if 0
|
||||
/* remove DQT2 */
|
||||
src_buf += 0x41;
|
||||
src_off += (2 + sec_len);
|
||||
#endif
|
||||
} else {
|
||||
copy_size = ((dst_len - dst_off) > copy_size) ? copy_size : (dst_len - dst_off);
|
||||
memcpy(dst_buf, src_buf, copy_size);
|
||||
src_buf += (2 + sec_len);
|
||||
src_off += (2 + sec_len);
|
||||
dst_buf += copy_size;
|
||||
dst_off += copy_size;
|
||||
}
|
||||
#else
|
||||
copy_size = ((dst_len - dst_off) > copy_size) ? copy_size : (dst_len - dst_off);
|
||||
memcpy(dst_buf, src_buf, copy_size);
|
||||
src_buf += (2 + sec_len);
|
||||
src_off += (2 + sec_len);
|
||||
dst_buf += copy_size;
|
||||
dst_off += copy_size;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case MARK_SOF0:
|
||||
//printf("MARK_SOF0\n");
|
||||
copy_size = (2 + sec_len);
|
||||
#if 0
|
||||
/* copy SOF0 and refactoring DQT */
|
||||
if ((dst_len - dst_off) > copy_size) {
|
||||
memcpy(dst_buf, src_buf, copy_size);
|
||||
dst_buf[copy_size - 1] = 0x01; /* refactoring DQT info */
|
||||
src_buf += (2 + sec_len);
|
||||
src_off += (2 + sec_len);
|
||||
dst_buf += copy_size;
|
||||
dst_off += copy_size;
|
||||
} else {
|
||||
copy_size = ((dst_len - dst_off) > copy_size) ? copy_size : (dst_len - dst_off);
|
||||
memcpy(dst_buf, src_buf, copy_size);
|
||||
src_buf += (2 + sec_len);
|
||||
src_off += (2 + sec_len);
|
||||
dst_buf += copy_size;
|
||||
dst_off += copy_size;
|
||||
}
|
||||
#else
|
||||
copy_size = ((dst_len - dst_off) > copy_size) ? copy_size : (dst_len - dst_off);
|
||||
memcpy(dst_buf, src_buf, copy_size);
|
||||
src_buf += (2 + sec_len);
|
||||
src_off += (2 + sec_len);
|
||||
dst_buf += copy_size;
|
||||
dst_off += copy_size;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case MARK_DHT:
|
||||
/* copy DHT*/
|
||||
//printf("MARK_DHT\n");
|
||||
copy_size = (2 + sec_len);
|
||||
#if SPLIT_DHT
|
||||
/* Split Huffman table */
|
||||
/* The Huffman table encoded by JPEG on the XMedia platform consists of four sets in a single DHT.
|
||||
* Some decoding platforms do not support this and need to split it into four sets */
|
||||
if ((dst_len - dst_off) > copy_size) {
|
||||
/* copy DHT0 */
|
||||
dst_buf[0] = 0xFF;
|
||||
dst_buf[1] = 0xC4;
|
||||
dst_buf[2] = 0x00;
|
||||
dst_buf[3] = 0x1F;
|
||||
dst_buf += 4;
|
||||
dst_off += 4;
|
||||
src_buf += 4;
|
||||
copy_size = 0x1F - 2;
|
||||
memcpy(dst_buf, src_buf, copy_size);
|
||||
dst_buf += copy_size;
|
||||
dst_off += copy_size;
|
||||
src_buf += copy_size;
|
||||
|
||||
/* copy DHT1 */
|
||||
dst_buf[0] = 0xFF;
|
||||
dst_buf[1] = 0xC4;
|
||||
dst_buf[2] = 0x00;
|
||||
dst_buf[3] = 0xB5;
|
||||
dst_buf += 4;
|
||||
dst_off += 4;
|
||||
copy_size = 0xB5 - 2;
|
||||
memcpy(dst_buf, src_buf, copy_size);
|
||||
dst_buf += copy_size;
|
||||
dst_off += copy_size;
|
||||
src_buf += copy_size;
|
||||
|
||||
/* copy DHT2 */
|
||||
dst_buf[0] = 0xFF;
|
||||
dst_buf[1] = 0xC4;
|
||||
dst_buf[2] = 0x00;
|
||||
dst_buf[3] = 0x1F;
|
||||
dst_buf += 4;
|
||||
dst_off += 4;
|
||||
copy_size = 0x1F - 2;
|
||||
memcpy(dst_buf, src_buf, copy_size);
|
||||
dst_buf += copy_size;
|
||||
dst_off += copy_size;
|
||||
src_buf += copy_size;
|
||||
|
||||
/* copy DHT3 */
|
||||
dst_buf[0] = 0xFF;
|
||||
dst_buf[1] = 0xC4;
|
||||
dst_buf[2] = 0x00;
|
||||
dst_buf[3] = 0xB5;
|
||||
dst_buf += 4;
|
||||
dst_off += 4;
|
||||
copy_size = 0xB5 - 2;
|
||||
memcpy(dst_buf, src_buf, copy_size);
|
||||
dst_buf += copy_size;
|
||||
dst_off += copy_size;
|
||||
src_buf += copy_size;
|
||||
|
||||
src_off += (2 + sec_len);
|
||||
} else {
|
||||
copy_size = ((dst_len - dst_off) > copy_size) ? copy_size : (dst_len - dst_off);
|
||||
memcpy(dst_buf, src_buf, copy_size);
|
||||
src_buf += (2 + sec_len);
|
||||
src_off += (2 + sec_len);
|
||||
dst_buf += copy_size;
|
||||
dst_off += copy_size;
|
||||
}
|
||||
#else
|
||||
copy_size = ((dst_len - dst_off) > copy_size) ? copy_size : (dst_len - dst_off);
|
||||
memcpy(dst_buf, src_buf, copy_size);
|
||||
src_buf += (2 + sec_len);
|
||||
src_off += (2 + sec_len);
|
||||
dst_buf += copy_size;
|
||||
dst_off += copy_size;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case MARK_DRI:
|
||||
//printf("MARK_DRI\n");
|
||||
copy_size = (2 + sec_len);
|
||||
#if REMOVE_DRI
|
||||
/* remove DRI */
|
||||
src_buf += copy_size;
|
||||
src_off += copy_size;
|
||||
#else
|
||||
copy_size = ((dst_len - dst_off) > copy_size) ? copy_size : (dst_len - dst_off);
|
||||
memcpy(dst_buf, src_buf, copy_size);
|
||||
src_buf += (2 + sec_len);
|
||||
src_off += (2 + sec_len);
|
||||
dst_buf += copy_size;
|
||||
dst_off += copy_size;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case MARK_SOS:
|
||||
/* copy SOS */
|
||||
//printf("MARK_SOS\n");
|
||||
case MARK_DOI:
|
||||
/* copy DOI */
|
||||
default:
|
||||
/* copy data */
|
||||
copy_size = src_len - src_off;
|
||||
copy_size = ((dst_len - dst_off) > copy_size) ? copy_size : (dst_len - dst_off);
|
||||
memcpy(dst_buf, src_buf, copy_size);
|
||||
dst_off += copy_size;
|
||||
*dest_offset = dst_off;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
*dest_offset = dst_off;
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* 序号 字段 字节 说明
|
||||
* 0 标识码 2 0xFFE1 ~ 0xFFEF
|
||||
* 1 数据长度 2 字段长度,不包括标识码,但包括本字段。JPEG标志定义,解码器可以根据长度跳过此标识段
|
||||
* 2 视频信息 1 码流格式, H264:0x01
|
||||
* 3 视频帧率 1 每秒帧数,如25表示25FPS
|
||||
* 4 保留信息 6 预留字段,默认都为0x00
|
||||
* 5 视频数据长度 2 视频数据字段长度,包括本字段
|
||||
* 6 视频数据 n 视频数据字节数 = (视频数据长度 - 2)
|
||||
*
|
||||
* 说明:每个字段(0xFFEX)仅支持填充(65535 - 12)字节数据。该封装协议支持最大填充(65523 * 15)字节数据
|
||||
*/
|
||||
|
||||
#define PROCOTOL_HEAD_SIZE 14
|
||||
#define SECTIONS_NUMS 9
|
||||
#define SECTIONS_SIZE (65535 - 12)
|
||||
#define APP1 0xFFE7
|
||||
|
||||
static unsigned char* fill_protocol_head(unsigned char* out, unsigned short id, unsigned char fps, unsigned short video_len)
|
||||
{
|
||||
unsigned char* pout = out;
|
||||
unsigned short val;
|
||||
|
||||
// Identification code
|
||||
val = id;
|
||||
*pout++ = (unsigned char)((val >> 8) & 0xFF);
|
||||
*pout++ = (unsigned char)(val & 0xFF);
|
||||
|
||||
// Data length
|
||||
val = 12 + video_len;
|
||||
*pout++ = (unsigned char)((val >> 8) & 0xFF);
|
||||
*pout++ = (unsigned char)(val & 0xFF);
|
||||
|
||||
// Video information
|
||||
*pout++ = (unsigned char)0x01; // 0x01: H264
|
||||
|
||||
// FPS
|
||||
*pout++ = fps;
|
||||
|
||||
// Reserve
|
||||
memset(pout, 0, 6);
|
||||
pout += 6;
|
||||
|
||||
// Video data length
|
||||
val = video_len + 2;
|
||||
*pout++ = (unsigned char)((val >> 8) & 0xFF);
|
||||
*pout++ = (unsigned char)(val & 0xFF);
|
||||
|
||||
return pout;
|
||||
}
|
||||
|
||||
static unsigned int merge_h264_to_jpeg(unsigned char* out, unsigned char* frame_h264,
|
||||
unsigned int size_h264, unsigned short fps)
|
||||
{
|
||||
unsigned int size;
|
||||
unsigned short i;
|
||||
unsigned char* pout = out;
|
||||
unsigned char* psrc = frame_h264;
|
||||
unsigned short id = (unsigned short)APP1;
|
||||
unsigned short sec_size = SECTIONS_SIZE;
|
||||
unsigned short sections = size_h264 / sec_size;
|
||||
unsigned short remaining = size_h264 % sec_size;
|
||||
|
||||
size = 0;
|
||||
for (i = 0; i < sections; i++) {
|
||||
pout = fill_protocol_head(pout, id, fps, sec_size);
|
||||
|
||||
// fill frame data
|
||||
memcpy(pout, psrc, sec_size);
|
||||
pout += sec_size;
|
||||
psrc += sec_size;
|
||||
|
||||
id += 1;
|
||||
size += (sec_size + PROCOTOL_HEAD_SIZE);
|
||||
}
|
||||
|
||||
if (remaining) {
|
||||
pout = fill_protocol_head(pout, id, fps, remaining);
|
||||
|
||||
// fill frame data
|
||||
memcpy(pout, psrc, remaining);
|
||||
pout += remaining;
|
||||
|
||||
id += 1;
|
||||
size += (remaining + PROCOTOL_HEAD_SIZE);
|
||||
}
|
||||
|
||||
//printf("merge h264 size(%u) to jpeg ok.\n", size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static unsigned char* refactoring_jpeg(unsigned char* frame_jpeg, unsigned int size_jpeg, unsigned int len_jpeg, unsigned int size_h264)
|
||||
{
|
||||
|
||||
unsigned char* ph264_start = NULL;
|
||||
unsigned char* pjpeg_old = NULL;
|
||||
unsigned char* pjpeg_new = NULL;
|
||||
|
||||
unsigned int size;
|
||||
unsigned int move_size;
|
||||
unsigned int h264_pack_size, total_pack_size;
|
||||
unsigned int sections_num;
|
||||
|
||||
sections_num = (size_h264 / SECTIONS_SIZE);
|
||||
if (size_h264 % SECTIONS_SIZE) {
|
||||
sections_num += 1;
|
||||
}
|
||||
h264_pack_size = (sections_num * PROCOTOL_HEAD_SIZE) + size_h264;
|
||||
total_pack_size = h264_pack_size + size_jpeg;
|
||||
|
||||
if ((total_pack_size > len_jpeg) || (sections_num > SECTIONS_NUMS)) {
|
||||
printf("[ERROR] H264 frame is too large, I will drop it!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ph264_start = frame_jpeg;
|
||||
|
||||
// skip 0xFFD8 and 0xFFE0
|
||||
ph264_start += 4;
|
||||
|
||||
// skip APP0 字段
|
||||
size = (ph264_start[0] << 8) & 0xFF00;
|
||||
size += (ph264_start[1] & 0xFF);
|
||||
ph264_start += size;
|
||||
|
||||
// Move the data after 'APP0 field' and leave space for H264
|
||||
pjpeg_old = ph264_start;
|
||||
pjpeg_new = (pjpeg_old + h264_pack_size);
|
||||
move_size = size_jpeg - 4 - size;
|
||||
memmove(pjpeg_new, pjpeg_old, move_size);
|
||||
|
||||
return ph264_start;
|
||||
}
|
||||
|
||||
/* @brief Package a frame of H264 into mjpeg
|
||||
*
|
||||
* @param [in, out] frame_jpeg Mjpeg frame buffer address
|
||||
* @param [in] size_jpeg Mjpeg frame size
|
||||
* @param [in] len_jpeg Mjpeg frame buffer size
|
||||
* @param [in] frame_h264 H264 frame buffer address
|
||||
* @param [in] size_h264 H264 frame size
|
||||
* @param [in] fps H264 FPS
|
||||
*
|
||||
* @return The total length of valid data in frame_jpeg.
|
||||
*/
|
||||
unsigned int pack_h264_to_jpeg(unsigned char* frame_jpeg, unsigned int size_jpeg, unsigned int len_jpeg,
|
||||
unsigned char* frame_h264, unsigned int size_h264, unsigned int fps)
|
||||
{
|
||||
unsigned int ret, total_size;
|
||||
unsigned char* ph264 = NULL;
|
||||
|
||||
total_size = size_jpeg;
|
||||
ph264 = refactoring_jpeg(frame_jpeg, size_jpeg, len_jpeg, size_h264);
|
||||
if (ph264 == NULL) {
|
||||
return total_size;
|
||||
}
|
||||
|
||||
ret = merge_h264_to_jpeg(ph264, frame_h264, size_h264, (unsigned short)fps);
|
||||
total_size += ret;
|
||||
|
||||
return total_size;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void dexdump(const unsigned char* str, unsigned int size)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
printf("0x%02X, ", str[i]);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main(unsigned int argc, unsigned char* argv[])
|
||||
{
|
||||
unsigned char frame_jpeg[2048] = {0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46,
|
||||
0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01,
|
||||
0x00, 0x01, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0xC5,
|
||||
0x00, 0x28, 0x1C, 0x1E, 0x23, 0x1E, 0x19, 0x28,
|
||||
0x23, 0x21, 0x23, 0x2D, 0x2B, 0x28, 0x30, 0x3C};
|
||||
unsigned char frame_h264[61];
|
||||
unsigned char i;
|
||||
unsigned int size;
|
||||
|
||||
for (i = 0; i < sizeof(frame_h264); i++) {
|
||||
frame_h264[i] = i;
|
||||
}
|
||||
|
||||
size = pack_h264_to_jpeg(frame_jpeg, 40, 1024, frame_h264, sizeof(frame_h264), 15);
|
||||
dexdump(frame_jpeg, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "video_stream.h"
|
||||
|
||||
#include "sample_video.h"
|
||||
#include "sample_video_yuv.h"
|
||||
#include "sample_video_mjpeg.h"
|
||||
#include "sample_video_h264.h"
|
||||
#include "sample_video_mjpeg_h264.h"
|
||||
|
||||
#include "venc_log.h"
|
||||
|
||||
#include "frame_cache.h"
|
||||
|
||||
#define XMEDIA_ACCESS_ENABLE 1
|
||||
|
||||
#define EMBEDDING_H24_IN_MJPEG 0
|
||||
|
||||
#if !XMEDIA_ACCESS_ENABLE
|
||||
#define STREAM_FILE_PATH "./stream.bin"
|
||||
#endif
|
||||
|
||||
#define v4l2_fourcc(a, b, c, d) \
|
||||
((uint32_t)(a) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
|
||||
|
||||
#define V4L2_PIX_FMT_YUYV v4l2_fourcc('Y', 'U', 'Y', 'V') /* 16 YUV 4:2:2 */
|
||||
#define V4L2_PIX_FMT_NV21 v4l2_fourcc('N', 'V', '2', '1') /* 12 Y/CrCb 4:2:0 */
|
||||
#define V4L2_PIX_FMT_NV12 v4l2_fourcc('N', 'V', '1', '2') /* 12 Y/CbCr 4:2:0 */
|
||||
#define V4L2_PIX_FMT_YUV420 v4l2_fourcc('Y', 'U', '1', '2') /* 12 YUV 4:2:0 */
|
||||
#define V4L2_PIX_FMT_YVU420 v4l2_fourcc('Y', 'V', '1', '2') /* 12 YUV 4:2:0 */
|
||||
#define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M', 'J', 'P', 'G') /* Motion-JPEG */
|
||||
#define V4L2_PIX_FMT_H264 v4l2_fourcc('H', '2', '6', '4') /* H264 with start codes */
|
||||
#define V4L2_PIX_FMT_H265 v4l2_fourcc('H', '2', '6', '5') /* H265 with start codes */
|
||||
|
||||
static int g_start = 0;
|
||||
static struct encoder_property g_encoder_property;
|
||||
|
||||
static xmedia_payload_type format_v4l2_to_mpp(uint32_t fcc)
|
||||
{
|
||||
xmedia_payload_type t;
|
||||
|
||||
switch (fcc) {
|
||||
case V4L2_PIX_FMT_YVU420:
|
||||
case V4L2_PIX_FMT_YUV420:
|
||||
case V4L2_PIX_FMT_NV21:
|
||||
case V4L2_PIX_FMT_NV12:
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
case V4L2_PIX_FMT_MJPEG:
|
||||
t = PT_MJPEG;
|
||||
break;
|
||||
|
||||
case V4L2_PIX_FMT_H264:
|
||||
t = PT_H264;
|
||||
break;
|
||||
|
||||
case V4L2_PIX_FMT_H265:
|
||||
t = PT_H265;
|
||||
break;
|
||||
|
||||
default:
|
||||
t = PT_MJPEG;
|
||||
break;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static void pic_format_convert(xmedia_payload_type *format)
|
||||
{
|
||||
*format = format_v4l2_to_mpp(g_encoder_property.format);
|
||||
}
|
||||
|
||||
static void pic_size_convert(xmedia_video_size *wh)
|
||||
{
|
||||
wh->width = g_encoder_property.width;
|
||||
wh->height = g_encoder_property.height;
|
||||
}
|
||||
|
||||
void get_user_format(xmedia_payload_type *format, xmedia_video_size *wh, unsigned int *framerate)
|
||||
{
|
||||
if (wh != NULL) {
|
||||
pic_size_convert(wh);
|
||||
}
|
||||
|
||||
if (format != NULL) {
|
||||
pic_format_convert(format);
|
||||
}
|
||||
|
||||
if (framerate != NULL) {
|
||||
*framerate = g_encoder_property.fps;
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* Processing Unit Operation Functions
|
||||
*******************************************************/
|
||||
|
||||
/* Processing Unit Operation Functions End */
|
||||
|
||||
/*******************************************************
|
||||
* Input Terminal Operation Functions
|
||||
*******************************************************/
|
||||
|
||||
/* Input Terminal Operation Functions End */
|
||||
|
||||
/*******************************************************
|
||||
* Stream Control Operation Functions
|
||||
*******************************************************/
|
||||
static int sample_video_stream_set_idr(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sample_video_stream_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sample_video_stream_deinit(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !XMEDIA_ACCESS_ENABLE
|
||||
static int g_stop = 0;
|
||||
static pthread_t send_pid = -1;
|
||||
static void* send_process(void* args)
|
||||
{
|
||||
FILE *fp = fopen(STREAM_FILE_PATH, "r");
|
||||
uvc_cache_t *uvc_cache = NULL;
|
||||
frame_node_t *fnode = NULL;
|
||||
|
||||
g_stop = 0;
|
||||
while (!g_stop && fp != NULL) {
|
||||
uvc_cache = uvc_cache_get();
|
||||
if (uvc_cache) {
|
||||
get_node_from_queue(uvc_cache->free_queue, &fnode);
|
||||
}
|
||||
|
||||
if (!fnode) {
|
||||
usleep(100000);
|
||||
} else {
|
||||
fseek(fp, 0L, SEEK_SET);
|
||||
fnode->used = fread((char*)fnode->mem, 1, fnode->length, fp);
|
||||
venc_logi("send %d\n", fnode->used);
|
||||
put_node_to_queue(uvc_cache->ok_queue, fnode);
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void start_send_file(int fcc)
|
||||
{
|
||||
pthread_create(&send_pid, NULL, &send_process, NULL);
|
||||
}
|
||||
|
||||
static void stop_send_file(int fcc)
|
||||
{
|
||||
g_stop = 1;
|
||||
|
||||
if (send_pid != -1) {
|
||||
pthread_join(send_pid, NULL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int sample_video_stream_startup(void)
|
||||
{
|
||||
int fcc = g_encoder_property.format;
|
||||
|
||||
venc_logi("sample stream startup.\n");
|
||||
g_start = 1;
|
||||
|
||||
#if !XMEDIA_ACCESS_ENABLE
|
||||
start_send_file(fcc);
|
||||
#else
|
||||
switch (fcc) {
|
||||
case V4L2_PIX_FMT_MJPEG:
|
||||
#if !EMBEDDING_H24_IN_MJPEG
|
||||
sample_mjpeg_init();
|
||||
sample_mjpeg_startup();
|
||||
#else
|
||||
sample_mjpeg_h264_init();
|
||||
sample_mjpeg_h264_startup();
|
||||
#endif
|
||||
break;
|
||||
|
||||
case V4L2_PIX_FMT_H264:
|
||||
case V4L2_PIX_FMT_H265:
|
||||
sample_h264_init();
|
||||
sample_h264_startup();
|
||||
break;
|
||||
|
||||
case V4L2_PIX_FMT_NV21:
|
||||
case V4L2_PIX_FMT_NV12:
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
case V4L2_PIX_FMT_YUV420:
|
||||
case V4L2_PIX_FMT_YVU420:
|
||||
sample_yuv_init();
|
||||
sample_yuv_startup(fcc);
|
||||
break;
|
||||
|
||||
default:
|
||||
venc_loge("Unsupported stream type.\n");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
venc_logi("sample stream startup ok.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sample_video_stream_shutdown(void)
|
||||
{
|
||||
int fcc = g_encoder_property.format;
|
||||
|
||||
if (!g_start) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !XMEDIA_ACCESS_ENABLE
|
||||
stop_send_file(fcc);
|
||||
#else
|
||||
switch (fcc) {
|
||||
case V4L2_PIX_FMT_MJPEG:
|
||||
#if !EMBEDDING_H24_IN_MJPEG
|
||||
sample_mjpeg_shutdown();
|
||||
#else
|
||||
sample_mjpeg_h264_shutdown();
|
||||
#endif
|
||||
break;
|
||||
|
||||
case V4L2_PIX_FMT_H264:
|
||||
case V4L2_PIX_FMT_H265:
|
||||
sample_h264_shutdown();
|
||||
break;
|
||||
|
||||
case V4L2_PIX_FMT_NV21:
|
||||
case V4L2_PIX_FMT_NV12:
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
case V4L2_PIX_FMT_YUV420:
|
||||
case V4L2_PIX_FMT_YVU420:
|
||||
sample_yuv_shutdown();
|
||||
break;
|
||||
|
||||
default:
|
||||
venc_loge("Unsupported stream type.\n");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
g_start = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sample_video_stream_set_property(struct encoder_property *p)
|
||||
{
|
||||
g_encoder_property = *p;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Stream Control Operation Functions End */
|
||||
|
||||
static struct stream_control_ops venc_sc_ops = {
|
||||
.init = &sample_video_stream_init,
|
||||
.deinit = &sample_video_stream_deinit,
|
||||
.startup = &sample_video_stream_startup,
|
||||
.shutdown = &sample_video_stream_shutdown,
|
||||
.set_idr = &sample_video_stream_set_idr,
|
||||
.set_property = &sample_video_stream_set_property,
|
||||
};
|
||||
|
||||
static struct processing_unit_ops venc_pu_ops = {
|
||||
/* get */
|
||||
.brightness_get = NULL,
|
||||
.contrast_get = NULL,
|
||||
.hue_get = NULL,
|
||||
.power_line_frequency_get = NULL,
|
||||
.saturation_get = NULL,
|
||||
.white_balance_temperature_auto_get = NULL,
|
||||
.white_balance_temperature_get = NULL,
|
||||
/* set */
|
||||
.brightness_set = NULL,
|
||||
.contrast_set = NULL,
|
||||
.hue_set = NULL,
|
||||
.power_line_frequency_set = NULL,
|
||||
.saturation_set = NULL,
|
||||
.white_balance_temperature_auto_set = NULL,
|
||||
.white_balance_temperature_set = NULL,
|
||||
};
|
||||
|
||||
static struct input_terminal_ops venc_it_ops = {
|
||||
/* get */
|
||||
.exposure_ansolute_time_get = NULL,
|
||||
.exposure_auto_mode_get = NULL,
|
||||
|
||||
/* set */
|
||||
.exposure_ansolute_time_set = NULL,
|
||||
.exposure_auto_mode_set = NULL,
|
||||
};
|
||||
|
||||
//static struct extension_unit_ops venc_xu_ops;
|
||||
|
||||
void video_stream_register(void)
|
||||
{
|
||||
stream_register_mpi_ops(&venc_sc_ops, &venc_pu_ops, &venc_it_ops, NULL);
|
||||
}
|
||||
|
||||
void video_stream_unregister(void)
|
||||
{
|
||||
stream_register_mpi_ops(NULL, NULL, NULL, NULL);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __SAMPLE_VIDEO_H__
|
||||
#define __SAMPLE_VIDEO_H__
|
||||
|
||||
#include "sample_comm.h"
|
||||
|
||||
struct encoder_property {
|
||||
unsigned int format;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int fps;
|
||||
};
|
||||
|
||||
void video_stream_register(void);
|
||||
void video_stream_unregister(void);
|
||||
|
||||
void get_user_format(xmedia_payload_type *format, xmedia_video_size *wh, unsigned int *framerate);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,851 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "sample_comm.h"
|
||||
#include "sample_comm_venc.h"
|
||||
#include "sample_comm_vpss.h"
|
||||
#include "sample_comm_vi.h"
|
||||
#include "sample_comm_sys.h"
|
||||
#include "sample_comm_isp.h"
|
||||
|
||||
#include "video_stream.h"
|
||||
#include "sample_video.h"
|
||||
#include "sample_video_dual.h"
|
||||
|
||||
#include "venc_log.h"
|
||||
#include "frame_cache.h"
|
||||
|
||||
static int g_start = 0;
|
||||
static pthread_t g_recv_pid;
|
||||
static sample_venc_getstream_para g_st_param;
|
||||
|
||||
//common
|
||||
struct dual_comm {
|
||||
sample_comm_sensor_type sensor_type;
|
||||
sample_comm_video_param video_param;
|
||||
};
|
||||
|
||||
// vi config
|
||||
struct dual_vi {
|
||||
xmedia_s32 vi_dev; /* 0:单板SENSOR0; 2:单板SENSOR2 */
|
||||
xmedia_s32 vi_chn;
|
||||
xmedia_s32 vi_pipe;
|
||||
sample_vi_config vi_cfg;
|
||||
};
|
||||
|
||||
// isp config
|
||||
struct dual_isp {
|
||||
sample_isp_param isp_param;
|
||||
};
|
||||
|
||||
// vpss config
|
||||
struct dual_vpss {
|
||||
xmedia_s32 vpss_pipe;
|
||||
xmedia_s32 vpss_ichn;
|
||||
xmedia_s32 vpss_ochn;
|
||||
sample_vpss_config vpss_cfg;
|
||||
};
|
||||
|
||||
// venc config
|
||||
struct dual_venc {
|
||||
xmedia_s32 venc_chn;
|
||||
sample_venc_config venc_cfg;
|
||||
};
|
||||
|
||||
struct dual_stream {
|
||||
xmedia_s32 stream_id;
|
||||
|
||||
struct dual_comm comm;
|
||||
struct dual_vi vi;
|
||||
struct dual_isp isp;
|
||||
struct dual_vpss vpss;
|
||||
struct dual_venc venc;
|
||||
};
|
||||
|
||||
#define STREAM_NUMS 4
|
||||
|
||||
static struct dual_stream g_stream[STREAM_NUMS];
|
||||
|
||||
static void sample_dual_config_comm(struct dual_comm* comm, sample_comm_sensor_type sns_type)
|
||||
{
|
||||
comm->sensor_type = sns_type;
|
||||
comm->video_param.video_fmt = XMEDIA_VIDEO_FMT_LINEAR;
|
||||
comm->video_param.pixel_fmt = XMEDIA_VIDEO_PIXEL_FMT_YVU_SEMIPLANAR_420;
|
||||
comm->video_param.data_width = XMEDIA_VIDEO_DATA_WIDTH_8;
|
||||
comm->video_param.compress_mode = XMEDIA_VIDEO_COMPRESS_MODE_NONE;
|
||||
}
|
||||
|
||||
static void sample_dual_config_vi(struct dual_vi* vi,
|
||||
xmedia_s32 vi_chn,
|
||||
xmedia_s32 vi_dev,
|
||||
xmedia_s32 vi_pipe,
|
||||
sample_comm_sensor_type sns_type)
|
||||
{
|
||||
vi->vi_chn = vi_chn;
|
||||
vi->vi_dev = vi_dev;
|
||||
vi->vi_pipe = vi_pipe;
|
||||
vi->vi_cfg.dev_info[vi_dev].dev_no = vi_dev;
|
||||
vi->vi_cfg.dev_info[vi_dev].dev_en = XMEDIA_TRUE;
|
||||
vi->vi_cfg.dev_info[vi_dev].sensor_type = sns_type;
|
||||
vi->vi_cfg.pipe_info[vi_pipe].pipe_no = vi_pipe;
|
||||
vi->vi_cfg.pipe_info[vi_pipe].pipe_en = XMEDIA_TRUE;
|
||||
vi->vi_cfg.pipe_info[vi_pipe].chn_info[vi_chn].chn_no = vi_chn;
|
||||
vi->vi_cfg.pipe_info[vi_pipe].chn_info[vi_chn].chn_en = XMEDIA_TRUE;
|
||||
vi->vi_cfg.dev_bind_pipe[vi_dev].pipe[0] = vi_pipe;
|
||||
vi->vi_cfg.dev_bind_pipe[vi_dev].pipe[1] = -1;
|
||||
}
|
||||
|
||||
static void sample_dual_config_isp(struct dual_isp* isp,
|
||||
xmedia_s32 vi_pipe,
|
||||
sample_comm_sensor_type sns_type)
|
||||
{
|
||||
xmedia_u32 framerate = 0;
|
||||
vi_sensor_info sensor_info = {0};
|
||||
|
||||
sample_comm_vi_get_sensor_info(sns_type, &sensor_info);
|
||||
sample_comm_vi_get_framerate_by_sensor(sns_type, &framerate);
|
||||
|
||||
// isp config init
|
||||
isp->isp_param.isp_info[vi_pipe].isp_config.fps = framerate;
|
||||
isp->isp_param.isp_info[vi_pipe].isp_config.mode_config.work_mode = XMEDIA_ISP_WORK_MODE_MASTER;
|
||||
isp->isp_param.isp_info[vi_pipe].isp_config.mode_config.master_mode.blend_stat_enable = XMEDIA_FALSE;
|
||||
isp->isp_param.isp_info[vi_pipe].isp_config.mode_config.master_mode.slave_num = 0;
|
||||
isp->isp_param.isp_info[vi_pipe].isp_config.pixel_fmt = sensor_info.pixel_format;
|
||||
isp->isp_param.isp_info[vi_pipe].isp_config.size.height = sensor_info.height;
|
||||
isp->isp_param.isp_info[vi_pipe].isp_config.size.width = sensor_info.width;
|
||||
isp->isp_param.isp_info[vi_pipe].isp_config.wdr_mode = sensor_info.wdr_mode;
|
||||
|
||||
// isp pipe init
|
||||
isp->isp_param.pipe[vi_pipe] = vi_pipe;
|
||||
isp->isp_param.isp_info[vi_pipe].flip = XMEDIA_FALSE;
|
||||
isp->isp_param.isp_info[vi_pipe].mirror = XMEDIA_FALSE;
|
||||
isp->isp_param.isp_info[vi_pipe].isp_pipe_en = XMEDIA_TRUE;
|
||||
isp->isp_param.isp_info[vi_pipe].isp_sensor_en = XMEDIA_TRUE;
|
||||
isp->isp_param.isp_info[vi_pipe].sensor_type = sns_type;
|
||||
}
|
||||
|
||||
static void sample_dual_config_vpss(struct dual_vpss* vpss,
|
||||
xmedia_s32 vpss_pipe,
|
||||
xmedia_s32 vpss_ichn,
|
||||
xmedia_s32 vpss_ochn)
|
||||
{
|
||||
vpss->vpss_pipe = vpss_pipe;
|
||||
vpss->vpss_ichn = vpss_ichn;
|
||||
vpss->vpss_ochn = vpss_ochn;
|
||||
//vpss->vpss_cfg = ;
|
||||
}
|
||||
|
||||
static void sample_dual_config_venc(struct dual_venc* venc,
|
||||
xmedia_s32 venc_chnl)
|
||||
{
|
||||
venc->venc_chn = venc_chnl;
|
||||
venc->venc_cfg.chn_info[venc_chnl].venc_en = XMEDIA_TRUE;
|
||||
venc->venc_cfg.chn_info[venc_chnl].venc_chn = venc_chnl;
|
||||
venc->venc_cfg.chn_info[venc_chnl].payload_type = PT_H264;
|
||||
venc->venc_cfg.chn_info[venc_chnl].rc_mode = VENC_RC_MODE_H264CBR;
|
||||
}
|
||||
|
||||
static void sammple_dual_stream_config(void)
|
||||
{
|
||||
xmedia_s32 id;
|
||||
xmedia_s32 vi_dev[STREAM_NUMS] = {0, 0, 2, 2};
|
||||
xmedia_s32 vi_pipe[STREAM_NUMS] = {0, 0, 1, 1};
|
||||
xmedia_s32 vi_chn[STREAM_NUMS] = {0, 0, 0, 0};
|
||||
xmedia_s32 vpss_pipe[STREAM_NUMS] = {0, 0, 1, 1};
|
||||
xmedia_s32 vpss_ichn[STREAM_NUMS] = {0, 0, 0, 0};
|
||||
xmedia_s32 vpss_ochn[STREAM_NUMS] = {0, 1, 0, 1};
|
||||
xmedia_s32 venc_chnl[STREAM_NUMS] = {0, 1, 2, 3};
|
||||
sample_comm_sensor_type sns_type[STREAM_NUMS] = {SENSOR0_TYPE, SENSOR0_TYPE, SENSOR1_TYPE, SENSOR1_TYPE};
|
||||
|
||||
for (id = 0; id < STREAM_NUMS; id++) {
|
||||
g_stream[id].stream_id = id;
|
||||
|
||||
sample_dual_config_comm(&g_stream[id].comm, sns_type[id]);
|
||||
sample_dual_config_vi(&g_stream[id].vi, vi_chn[id], vi_dev[id], vi_pipe[id], sns_type[id]);
|
||||
sample_dual_config_isp(&g_stream[id].isp, vi_pipe[id], sns_type[id]);
|
||||
sample_dual_config_vpss(&g_stream[id].vpss, vpss_pipe[id], vpss_ichn[id], vpss_ochn[id]);
|
||||
sample_dual_config_venc(&g_stream[id].venc, venc_chnl[id]);
|
||||
}
|
||||
}
|
||||
|
||||
static int sample_dual_sys_init(void)
|
||||
{
|
||||
xmedia_s32 ret;
|
||||
xmedia_s32 id;
|
||||
xmedia_s32 blk_size, pool_id;
|
||||
xmedia_s32 vi_pipe;
|
||||
sample_comm_sensor_type sensor_type;
|
||||
vi_sensor_info sensor_info = {0};
|
||||
xmedia_video_size pic_size = {0};
|
||||
sample_sys_config sys_config = {0};
|
||||
sample_comm_video_param* video_param = NULL;
|
||||
|
||||
func_entry();
|
||||
|
||||
sammple_dual_stream_config();
|
||||
|
||||
pool_id = 0;
|
||||
|
||||
for (id = 0; id < STREAM_NUMS; id++) {
|
||||
sensor_type = g_stream[id].comm.sensor_type;
|
||||
|
||||
sample_comm_vi_get_sensor_info(sensor_type, &sensor_info);
|
||||
|
||||
// sys init
|
||||
vi_pipe = g_stream[id].vi.vi_pipe;
|
||||
sys_config.sys_conf.pipe_mode[vi_pipe].vicap_viproc_mode = XMEDIA_WORK_MODE_OFFLINE;
|
||||
sys_config.sys_conf.pipe_mode[vi_pipe].viproc_vpss_mode = XMEDIA_WORK_MODE_OFFLINE;
|
||||
sys_config.sys_conf.pipe_mode[vi_pipe].gdc_vpss_mode = XMEDIA_WORK_MODE_OFFLINE;
|
||||
|
||||
pic_size.width = sensor_info.width;
|
||||
pic_size.height = sensor_info.height;
|
||||
|
||||
venc_logd("resolution: w(%d) x h(%d).\n", pic_size.width, pic_size.height);
|
||||
|
||||
video_param = &g_stream[id].comm.video_param;
|
||||
blk_size = sample_comm_sys_get_buffer_size(pic_size,
|
||||
video_param->video_fmt,
|
||||
sensor_info.pixel_format,
|
||||
sensor_info.bit_width,
|
||||
video_param->compress_mode);
|
||||
sys_config.vb_conf.common_pool[pool_id].block_size = blk_size;
|
||||
sys_config.vb_conf.common_pool[pool_id].block_cnt = 5;
|
||||
venc_logd("allocate cache pool %d: size(%d) x cnt(%d)\n", pool_id, blk_size, 5);
|
||||
pool_id++;
|
||||
|
||||
blk_size = sample_comm_sys_get_buffer_size(pic_size,
|
||||
video_param->video_fmt,
|
||||
video_param->pixel_fmt,
|
||||
video_param->data_width,
|
||||
video_param->compress_mode);
|
||||
sys_config.vb_conf.common_pool[pool_id].block_size = blk_size;
|
||||
sys_config.vb_conf.common_pool[pool_id].block_cnt = 5;
|
||||
venc_logd("allocate cache pool %d: size(%d) x cnt(%d)\n", pool_id, blk_size, 5);
|
||||
pool_id++;
|
||||
}
|
||||
|
||||
sys_config.vb_conf.supplement_config = (1 << 0);
|
||||
sys_config.vb_conf.supplement_config |= (1 << 1);
|
||||
|
||||
ret = sample_comm_sys_init(&sys_config);
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("sample_comm_sys_init failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
//in online-online mode,vi and vpss must be reset at the same time
|
||||
ret = sample_comm_vi_init();
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("sample_comm_vi_init failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sample_comm_vpss_init();
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("sample_comm_vpss_init failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sample_comm_venc_init();
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("sample_comm_venc_init failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
func_success();
|
||||
|
||||
return XMEDIA_SUCCESS;
|
||||
}
|
||||
|
||||
static int sample_dual_sys_deinit(void)
|
||||
{
|
||||
sample_comm_venc_exit();
|
||||
|
||||
sample_comm_vpss_exit();
|
||||
|
||||
sample_comm_vi_exit();
|
||||
|
||||
sample_comm_sys_exit();
|
||||
|
||||
return XMEDIA_SUCCESS;
|
||||
}
|
||||
|
||||
static FILE* fp[4] = {NULL, NULL, NULL, NULL};
|
||||
char filename[4][32] = {
|
||||
{"stream_dev.h264"},
|
||||
{"stream_1.h264"},
|
||||
{"stream_2.h264"},
|
||||
{"stream_3.h264"}
|
||||
};
|
||||
|
||||
struct dual_head {
|
||||
unsigned int id;
|
||||
unsigned int stream_offset;
|
||||
unsigned int stream_size;
|
||||
unsigned char reserve[4];
|
||||
};
|
||||
|
||||
void save_stream(char* stream, int length)
|
||||
{
|
||||
int w_actl = 0;
|
||||
int recv_size = length;
|
||||
unsigned int offset, size;
|
||||
unsigned int id = 0;
|
||||
unsigned char* tmp = (unsigned char*)stream;
|
||||
struct dual_head* stream_head = NULL;
|
||||
|
||||
while (recv_size > 0) {
|
||||
stream_head = (struct dual_head*)stream;
|
||||
id = stream_head->id;
|
||||
offset = stream_head->stream_offset;
|
||||
size = stream_head->stream_size;
|
||||
|
||||
if (id >= STREAM_NUMS) {
|
||||
printf("Error: Invalid stream id.\n");
|
||||
return ;
|
||||
}
|
||||
|
||||
//printf("id(%u), off(%u), size(%u)\n", id, offset, size);
|
||||
tmp += offset;
|
||||
if (fp[id] != NULL) {
|
||||
w_actl = fwrite(tmp, 1, size, fp[id]);
|
||||
}
|
||||
|
||||
if (w_actl != size) {
|
||||
//printf("Warning: exp(%d) != acl(%d).", size, w_actl);
|
||||
}
|
||||
|
||||
tmp += size;
|
||||
|
||||
recv_size -= (offset + size);
|
||||
}
|
||||
}
|
||||
|
||||
static int sample_dual_save_data(xmedia_venc_stream* pst_stream, int id)
|
||||
{
|
||||
int i = 0;
|
||||
xmedia_venc_pack *pst_data = NULL;
|
||||
unsigned char *s = NULL;
|
||||
unsigned int data_len = 0;
|
||||
unsigned int copy_size = 0;
|
||||
struct dual_head head = {0};
|
||||
|
||||
uvc_cache_t *uvc_cache = uvc_cache_get();
|
||||
frame_node_t *fnode = NULL;
|
||||
|
||||
if (uvc_cache) {
|
||||
get_node_from_queue(uvc_cache->free_queue, &fnode);
|
||||
}
|
||||
|
||||
if (!fnode) {
|
||||
venc_logd("drop frame.\n");
|
||||
return XMEDIA_SUCCESS;
|
||||
}
|
||||
|
||||
fnode->used = sizeof(struct dual_head);
|
||||
|
||||
head.id = id;
|
||||
head.stream_offset = sizeof(struct dual_head);
|
||||
head.stream_size = 0;
|
||||
|
||||
for (i = 0; i < pst_stream->pack_count; ++i) {
|
||||
pst_data = &pst_stream->pack[i];
|
||||
s = pst_data->vir_addr + pst_data->offset;
|
||||
data_len = pst_data->len - pst_data->offset;
|
||||
copy_size = data_len < (fnode->length - fnode->used) ? data_len : (fnode->length - fnode->used);
|
||||
|
||||
if (copy_size > 0) {
|
||||
memcpy(fnode->mem + fnode->used, s, copy_size);
|
||||
fnode->used += copy_size;
|
||||
head.stream_size += copy_size;
|
||||
}
|
||||
|
||||
if (data_len > copy_size) {
|
||||
venc_logw("fnode->length = %u\n", fnode->length);
|
||||
venc_logw("WARN: missing pack. exp(%u) != act(%u)\n", data_len, copy_size);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(fnode->mem, (void*)&head, sizeof(struct dual_head));
|
||||
|
||||
//fwrite((char*)(fnode->mem), 1, 16, fp[0]);
|
||||
if (fp[id] != NULL) {
|
||||
//fwrite((char*)(fnode->mem + head.stream_offset), 1, head.stream_size, fp[id]);
|
||||
//save_stream((char*)fnode->mem, fnode->used);
|
||||
}
|
||||
|
||||
venc_logd("send size(%u)\n", fnode->used);
|
||||
put_node_to_queue(uvc_cache->ok_queue, fnode);
|
||||
|
||||
return XMEDIA_SUCCESS;
|
||||
}
|
||||
|
||||
static int sample_dual_save_stream(xmedia_payload_type en_type, xmedia_venc_stream *pst_stream, int id)
|
||||
{
|
||||
xmedia_s32 ret = XMEDIA_FAILURE;
|
||||
|
||||
if (PT_H264 == en_type) {
|
||||
ret = sample_dual_save_data(pst_stream, id);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void* sample_dual_stream_proc(void* arg)
|
||||
{
|
||||
xmedia_s32 i;
|
||||
xmedia_s32 ret = XMEDIA_FAILURE;
|
||||
|
||||
xmedia_venc_chn_attr venc_chn_attr;
|
||||
xmedia_payload_type payload_type[VENC_MAX_CHN_NUM];
|
||||
sample_venc_getstream_para *param = (sample_venc_getstream_para*)arg;
|
||||
xmedia_s32 chn_cnt = param->cnt;
|
||||
|
||||
struct timeval time_out;
|
||||
xmedia_venc_stream venc_stream;
|
||||
xmedia_venc_chn_status stat;
|
||||
xmedia_u32 venc_mask = 0;
|
||||
|
||||
if (chn_cnt >= VENC_MAX_CHN_NUM) {
|
||||
venc_loge("input venc chn count invaild\n");
|
||||
return XMEDIA_NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < chn_cnt; ++i) {
|
||||
if (param->venc_chn[i] < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = xmedia_venc_get_chn_attr(param->venc_chn[i], &venc_chn_attr);
|
||||
if (XMEDIA_SUCCESS != ret) {
|
||||
venc_loge("xmedia_venc_get_chn_attr chn[%d] failed with %#x!\n", param->venc_chn[i], ret);
|
||||
return XMEDIA_NULL;
|
||||
}
|
||||
|
||||
payload_type[i] = venc_chn_attr.venc_attr.en_type;
|
||||
venc_mask |= 1 << param->venc_chn[i];
|
||||
if (fp[i] == NULL) {
|
||||
fp[i] = fopen((char*)filename[i], "w+");
|
||||
}
|
||||
}
|
||||
|
||||
venc_logi("Dual start.\n");
|
||||
while (param->thread_start) {
|
||||
time_out.tv_sec = 2;
|
||||
time_out.tv_usec = 0;
|
||||
ret = xmedia_venc_select(venc_mask, &time_out);
|
||||
if (ret == XMEDIA_ERRCODE_INVALID_PARAM || ret == XMEDIA_FAILURE) {
|
||||
venc_loge("select err\n");
|
||||
break;
|
||||
} else if (ret == XMEDIA_ERRCODE_TIMEOUT) {
|
||||
param->stream_timeout_cnt++;
|
||||
venc_logd("get venc stream time out, continue. \n");
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i < chn_cnt; ++i) {
|
||||
if (param->venc_chn[i] < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = xmedia_venc_query_status(param->venc_chn[i], &stat);
|
||||
if (XMEDIA_SUCCESS != ret) {
|
||||
venc_logd("xmedia_venc_query_status chn[%d] failed with %#x!\n", i, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
if (0 == stat.cur_packs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
memset(&venc_stream, 0, sizeof(venc_stream));
|
||||
venc_stream.pack = (xmedia_venc_pack*)malloc(sizeof(xmedia_venc_pack) * stat.cur_packs);
|
||||
if (XMEDIA_NULL == venc_stream.pack) {
|
||||
venc_logd("malloc stream pack failed!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
venc_stream.pack_count = stat.cur_packs;
|
||||
ret = xmedia_venc_get_stream(param->venc_chn[i], &venc_stream, XMEDIA_TRUE);
|
||||
if (XMEDIA_SUCCESS != ret) {
|
||||
free(venc_stream.pack);
|
||||
venc_stream.pack = XMEDIA_NULL;
|
||||
xmedia_venc_release_stream(param->venc_chn[i], &venc_stream);
|
||||
venc_loge("xmedia_venc_get_stream failed with %#x!\n", ret);
|
||||
break;
|
||||
}
|
||||
|
||||
sample_dual_save_stream(payload_type[i], &venc_stream, i);
|
||||
|
||||
ret = xmedia_venc_release_stream(param->venc_chn[i], &venc_stream);
|
||||
if (XMEDIA_SUCCESS != ret) {
|
||||
venc_loge("xmedia_venc_release_stream failed!\n");
|
||||
free(venc_stream.pack);
|
||||
venc_stream.pack = XMEDIA_NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
free(venc_stream.pack);
|
||||
venc_stream.pack = XMEDIA_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (fp[i] != NULL) {
|
||||
fclose(fp[i]);
|
||||
}
|
||||
|
||||
fp[i] = NULL;
|
||||
}
|
||||
|
||||
venc_logi("Dual stream exit.\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int sample_dual_start_get_stream()
|
||||
{
|
||||
int i, rc;
|
||||
|
||||
g_st_param.cnt = STREAM_NUMS;
|
||||
g_st_param.thread_start = XMEDIA_TRUE;
|
||||
|
||||
for (i = 0; i < STREAM_NUMS; i++) {
|
||||
g_st_param.venc_chn[i] = g_stream[i].venc.venc_chn;
|
||||
}
|
||||
|
||||
rc = pthread_create(&g_recv_pid, NULL, sample_dual_stream_proc, (void*)&g_st_param);
|
||||
if (rc < 0) {
|
||||
venc_loge("Create sample_dual_stream_proc failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sample_dual_stop_get_stream(void)
|
||||
{
|
||||
if (XMEDIA_TRUE == g_st_param.thread_start) {
|
||||
g_st_param.thread_start = XMEDIA_FALSE;
|
||||
pthread_join(g_recv_pid, 0);
|
||||
}
|
||||
|
||||
venc_logi("stop get Dual stream ok.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sample_dual_normalp_classic(void)
|
||||
{
|
||||
xmedia_s32 id;
|
||||
xmedia_s32 ret = XMEDIA_SUCCESS;
|
||||
xmedia_u32 framerate = 0;
|
||||
xmedia_video_size pic_size = {0};
|
||||
xmedia_video_size vpss_ochn_size = {0};
|
||||
//xmedia_isp_config isp_config = {0};
|
||||
xmedia_payload_type en_payload = PT_H264;
|
||||
vi_sensor_info sensor_info = {0};
|
||||
|
||||
xmedia_s32 venc_chn_0, venc_chn_1;
|
||||
xmedia_s32 vi_pipe, vi_chn;
|
||||
xmedia_s32 vpss_pipe, vpss_ichn, vpss_ochn_0, vpss_ochn_1;
|
||||
sample_comm_sensor_type sensor_type;
|
||||
sample_isp_param* isp_param = NULL;
|
||||
sample_vi_config* vi_config = NULL;
|
||||
sample_vpss_config* vpss_config = NULL;
|
||||
sample_venc_config* venc_config = NULL;
|
||||
sample_comm_video_param* video_param = NULL;
|
||||
|
||||
for (id = 0; id < STREAM_NUMS; id++) {
|
||||
#if 0
|
||||
g_vi_config.dev_info[g_vi_dev].dev_en = XMEDIA_TRUE;
|
||||
g_vi_config.dev_info[g_vi_dev].dev_no = g_vi_dev;
|
||||
g_vi_config.dev_info[g_vi_dev].sensor_type = g_sensor_type;
|
||||
g_vi_config.pipe_info[g_vi_pipe].pipe_en = XMEDIA_TRUE;
|
||||
g_vi_config.pipe_info[g_vi_pipe].pipe_no = g_vi_pipe;
|
||||
g_vi_config.pipe_info[g_vi_pipe].chn_info[g_vi_chn].chn_en = XMEDIA_TRUE;
|
||||
g_vi_config.pipe_info[g_vi_pipe].chn_info[g_vi_chn].chn_no = g_vi_chn;
|
||||
g_vi_config.dev_bind_pipe[g_vi_dev].pipe[0] = g_vi_pipe;
|
||||
g_vi_config.dev_bind_pipe[g_vi_dev].pipe[1] = -1;
|
||||
#endif
|
||||
sensor_type = g_stream[id].comm.sensor_type;
|
||||
|
||||
sample_comm_vi_get_sensor_info(sensor_type, &sensor_info);
|
||||
sample_comm_vi_get_framerate_by_sensor(sensor_type, &framerate);
|
||||
|
||||
pic_size.height = sensor_info.height;
|
||||
pic_size.width = sensor_info.width;
|
||||
|
||||
#if 0
|
||||
isp_config.fps = framerate;
|
||||
isp_config.mode_config.work_mode = XMEDIA_ISP_WORK_MODE_MASTER;
|
||||
isp_config.mode_config.master_mode.blend_stat_enable = XMEDIA_FALSE;
|
||||
isp_config.mode_config.master_mode.slave_num = 0;
|
||||
isp_config.pixel_fmt = sensor_info.pixel_format;
|
||||
isp_config.size.height = sensor_info.height;
|
||||
isp_config.size.width = sensor_info.width;
|
||||
isp_config.wdr_mode = sensor_info.wdr_mode;
|
||||
|
||||
// isp pipe init
|
||||
g_isp_param.pipe[g_vi_pipe] = g_vi_pipe;
|
||||
g_isp_param.isp_info[g_vi_pipe].sensor_type = g_sensor_type;
|
||||
g_isp_param.isp_info[g_vi_pipe].flip = XMEDIA_FALSE;
|
||||
g_isp_param.isp_info[g_vi_pipe].mirror = XMEDIA_FALSE;
|
||||
g_isp_param.isp_info[g_vi_pipe].isp_pipe_en = XMEDIA_TRUE;
|
||||
g_isp_param.isp_info[g_vi_pipe].isp_sensor_en = XMEDIA_TRUE;
|
||||
|
||||
memcpy(&(g_isp_param.isp_info[g_vi_pipe].isp_config), &isp_config, sizeof(xmedia_isp_config));
|
||||
#endif
|
||||
|
||||
// isp init
|
||||
isp_param = &g_stream[id].isp.isp_param;
|
||||
vi_config = &g_stream[id].vi.vi_cfg;
|
||||
|
||||
ret = sample_comm_isp_init(isp_param, vi_config);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("stream[%d] isp init failed!\n", id);
|
||||
}
|
||||
|
||||
// vi start
|
||||
vi_config = &g_stream[id].vi.vi_cfg;
|
||||
video_param = &g_stream[id].comm.video_param;
|
||||
|
||||
ret = sample_comm_vi_start(vi_config, video_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("stream[%d] start vi failed!\n", id);
|
||||
}
|
||||
|
||||
// isp start
|
||||
isp_param = &g_stream[id].isp.isp_param;
|
||||
|
||||
ret = sample_comm_isp_start(isp_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("stream[%d] start isp failed!\n", id);
|
||||
}
|
||||
|
||||
// vpss config
|
||||
vpss_pipe = g_stream[id].vpss.vpss_pipe;
|
||||
vpss_ichn = g_stream[id].vpss.vpss_ichn;
|
||||
vpss_ochn_0 = g_stream[id].vpss.vpss_ochn;
|
||||
vpss_ochn_1 = g_stream[id + 1].vpss.vpss_ochn;
|
||||
vpss_config = &g_stream[id].vpss.vpss_cfg;
|
||||
video_param = &g_stream[id].comm.video_param;
|
||||
|
||||
ret = sample_comm_vpss_get_default_pipe_cfg(&vpss_config->pipe_info[vpss_pipe].pipe_config,
|
||||
pic_size,
|
||||
video_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("stream[%d] get default pipe cfg failed !\n", id);
|
||||
}
|
||||
|
||||
get_user_format(&en_payload, &pic_size, NULL);
|
||||
vpss_ochn_size.width = pic_size.width;
|
||||
vpss_ochn_size.height = pic_size.height;
|
||||
|
||||
vpss_config->pipe_info[vpss_pipe].pipe_en = XMEDIA_TRUE;
|
||||
vpss_config->pipe_info[vpss_pipe].pipe_no = vpss_pipe;
|
||||
#if 0
|
||||
vpss_config->pipe_info[vpss_pipe].chn_info[vpss_ochn].chn_no = vpss_ochn;
|
||||
vpss_config->pipe_info[vpss_pipe].chn_info[vpss_ochn].chn_en = XMEDIA_TRUE;
|
||||
#else
|
||||
vpss_config->pipe_info[vpss_pipe].chn_info[vpss_ochn_0].chn_no = vpss_ochn_0;
|
||||
vpss_config->pipe_info[vpss_pipe].chn_info[vpss_ochn_0].chn_en = XMEDIA_TRUE;
|
||||
vpss_config->pipe_info[vpss_pipe].chn_info[vpss_ochn_1].chn_no = vpss_ochn_1;
|
||||
vpss_config->pipe_info[vpss_pipe].chn_info[vpss_ochn_1].chn_en = XMEDIA_TRUE;
|
||||
#endif
|
||||
#if 0
|
||||
ret = sample_comm_vpss_get_default_ochn_cfg(
|
||||
&vpss_config->pipe_info[vpss_pipe].chn_info[vpss_ochn].chn_config,
|
||||
vpss_ochn_size,
|
||||
video_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("stream[%d] get default pipe cfg failed !\n", id);
|
||||
}
|
||||
#else
|
||||
ret = sample_comm_vpss_get_default_ochn_cfg(
|
||||
&vpss_config->pipe_info[vpss_pipe].chn_info[vpss_ochn_0].chn_config,
|
||||
vpss_ochn_size,
|
||||
video_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("stream[%d] get default pipe cfg failed !\n", id);
|
||||
}
|
||||
|
||||
ret = sample_comm_vpss_get_default_ochn_cfg(
|
||||
&vpss_config->pipe_info[vpss_pipe].chn_info[vpss_ochn_1].chn_config,
|
||||
vpss_ochn_size,
|
||||
video_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("stream[%d] get default pipe cfg failed !\n", id + 1);
|
||||
}
|
||||
#endif
|
||||
// vpss start
|
||||
ret = sample_comm_vpss_start(vpss_config);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("stream[%d] vpss start failed !\n", id);
|
||||
}
|
||||
|
||||
vi_chn = g_stream[id].vi.vi_chn;
|
||||
vi_pipe = g_stream[id].vi.vi_pipe;
|
||||
vpss_ichn = g_stream[id].vpss.vpss_ichn;
|
||||
vpss_pipe = g_stream[id].vpss.vpss_pipe;
|
||||
ret = sample_comm_sys_vi_bind_vpss(vi_pipe, vi_chn, vpss_pipe, vpss_ichn);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("stream[%d] vi bind vpss failed !\n", id);
|
||||
}
|
||||
|
||||
//venc init
|
||||
venc_chn_0 = g_stream[id].venc.venc_chn;
|
||||
venc_config = &g_stream[id].venc.venc_cfg;
|
||||
|
||||
venc_config->chn_info[venc_chn_0].venc_chn = venc_chn_0;
|
||||
venc_config->chn_info[venc_chn_0].venc_en = XMEDIA_TRUE;
|
||||
venc_config->chn_info[venc_chn_0].payload_type = en_payload;
|
||||
venc_config->chn_info[venc_chn_0].rc_mode = VENC_RC_MODE_H264CBR;
|
||||
|
||||
sample_comm_venc_get_default_chn_info(vpss_ochn_size,
|
||||
framerate,
|
||||
&venc_config->chn_info[venc_chn_0]);
|
||||
|
||||
ret = sample_comm_venc_start(venc_config);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("stream[%d] venc start failed !\n", id);
|
||||
}
|
||||
|
||||
venc_chn_1 = g_stream[id + 1].venc.venc_chn;
|
||||
venc_config = &g_stream[id + 1].venc.venc_cfg;
|
||||
|
||||
venc_config->chn_info[venc_chn_1].venc_chn = venc_chn_1;
|
||||
venc_config->chn_info[venc_chn_1].venc_en = XMEDIA_TRUE;
|
||||
venc_config->chn_info[venc_chn_1].payload_type = en_payload;
|
||||
venc_config->chn_info[venc_chn_1].rc_mode = VENC_RC_MODE_H264CBR;
|
||||
|
||||
sample_comm_venc_get_default_chn_info(vpss_ochn_size,
|
||||
framerate,
|
||||
&venc_config->chn_info[venc_chn_1]);
|
||||
|
||||
ret = sample_comm_venc_start(venc_config);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("stream[%d] venc start failed !\n", id);
|
||||
}
|
||||
|
||||
ret = sample_comm_sys_vpss_bind_venc(vpss_pipe, vpss_ochn_0, venc_chn_0);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("stream[%d] vpss bind venc failed !\n", id);
|
||||
}
|
||||
|
||||
ret = sample_comm_sys_vpss_bind_venc(vpss_pipe, vpss_ochn_1, venc_chn_1);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("stream[%d] vpss bind venc failed !\n", id);
|
||||
}
|
||||
|
||||
id++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* Processing Unit Operation Functions
|
||||
*******************************************************/
|
||||
|
||||
/* Processing Unit Operation Functions End */
|
||||
|
||||
/*******************************************************
|
||||
* Input Terminal Operation Functions
|
||||
*******************************************************/
|
||||
|
||||
/* Input Terminal Operation Functions End */
|
||||
|
||||
/*******************************************************
|
||||
* Stream Control Operation Functions
|
||||
*******************************************************/
|
||||
int sample_dual_set_idr(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_dual_init(void)
|
||||
{
|
||||
sample_dual_sys_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_dual_startup(void)
|
||||
{
|
||||
venc_logi("Dual stream startup.\n");
|
||||
g_start = 1;
|
||||
|
||||
if (sample_dual_normalp_classic() < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
sample_dual_start_get_stream();
|
||||
venc_logi("Dual stream startup ok.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_dual_shutdown(void)
|
||||
{
|
||||
xmedia_s32 id;
|
||||
xmedia_s32 vi_pipe, vi_chn;
|
||||
xmedia_s32 vpss_pipe, vpss_ichn, vpss_ochn_0, vpss_ochn_1;
|
||||
xmedia_s32 venc_chn_0, venc_chn_1;
|
||||
sample_venc_config* venc_config = NULL;
|
||||
sample_vpss_config* vpss_config = NULL;
|
||||
sample_isp_param* isp_param = NULL;
|
||||
sample_vi_config* vi_config = NULL;
|
||||
|
||||
if (!g_start) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sample_dual_stop_get_stream();
|
||||
|
||||
for (id = 0; id < STREAM_NUMS; id++) {
|
||||
venc_config = &g_stream[id].venc.venc_cfg;
|
||||
sample_comm_venc_stop(venc_config);
|
||||
|
||||
venc_config = &g_stream[id + 1].venc.venc_cfg;
|
||||
sample_comm_venc_stop(venc_config);
|
||||
|
||||
vpss_pipe = g_stream[id].vpss.vpss_pipe;
|
||||
vpss_ichn = g_stream[id].vpss.vpss_ichn;
|
||||
vpss_ochn_0 = g_stream[id].vpss.vpss_ochn;
|
||||
vpss_ochn_1 = g_stream[id + 1].vpss.vpss_ochn;
|
||||
venc_chn_0 = g_stream[id].venc.venc_chn;
|
||||
venc_chn_1 = g_stream[id + 1].venc.venc_chn;
|
||||
|
||||
sample_comm_sys_vpss_unbind_venc(vpss_pipe, vpss_ochn_0, venc_chn_0);
|
||||
sample_comm_sys_vpss_unbind_venc(vpss_pipe, vpss_ochn_1, venc_chn_1);
|
||||
|
||||
vi_pipe = g_stream[id].vi.vi_pipe;
|
||||
vi_chn = g_stream[id].vi.vi_chn;
|
||||
sample_comm_sys_vi_unbind_vpss(vi_pipe, vi_chn, vpss_pipe, vpss_ichn);
|
||||
|
||||
vpss_config = &g_stream[id].vpss.vpss_cfg;
|
||||
sample_comm_vpss_stop(vpss_config);
|
||||
|
||||
isp_param = &g_stream[id].isp.isp_param;
|
||||
sample_comm_isp_stop(isp_param);
|
||||
|
||||
vi_config = &g_stream[id].vi.vi_cfg;
|
||||
sample_comm_vi_stop(vi_config);
|
||||
|
||||
sample_comm_isp_exit(isp_param);
|
||||
|
||||
id++;
|
||||
}
|
||||
|
||||
sample_dual_sys_deinit();
|
||||
|
||||
g_start = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __SAMPLE_VIDEO_DUAL_H__
|
||||
#define __SAMPLE_VIDEO_DUAL_H__
|
||||
|
||||
int sample_dual_set_idr(void);
|
||||
int sample_dual_init(void);
|
||||
int sample_dual_startup(void);
|
||||
int sample_dual_shutdown(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,561 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "sample_comm.h"
|
||||
#include "sample_comm_venc.h"
|
||||
#include "sample_comm_vpss.h"
|
||||
#include "sample_comm_vi.h"
|
||||
#include "sample_comm_sys.h"
|
||||
#include "sample_comm_isp.h"
|
||||
|
||||
#include "video_stream.h"
|
||||
#include "sample_video.h"
|
||||
#include "sample_video_h264.h"
|
||||
|
||||
#include "venc_log.h"
|
||||
#include "frame_cache.h"
|
||||
|
||||
static int g_start = 0;
|
||||
static pthread_t g_recv_pid;
|
||||
static sample_venc_getstream_para g_st_param;
|
||||
|
||||
//common
|
||||
static sample_comm_sensor_type g_sensor_type = SENSOR0_TYPE;
|
||||
static sample_comm_video_param g_video_param = {0};
|
||||
|
||||
// vi config
|
||||
static xmedia_s32 g_vi_dev = 0; /* 0:单板SENSOR0; 2:单板SENSOR2 */
|
||||
static xmedia_s32 g_vi_chn = 0;
|
||||
static xmedia_s32 g_vi_pipe = 0;
|
||||
static sample_vi_config g_vi_config = {0};
|
||||
|
||||
// isp config
|
||||
static sample_isp_param g_isp_param = {0};
|
||||
|
||||
// vpss config
|
||||
static xmedia_s32 g_vpss_pipe = 0;
|
||||
static xmedia_s32 g_vpss_ichn = 0;
|
||||
static xmedia_s32 g_vpss_ochn = 0;
|
||||
static sample_vpss_config g_vpss_config = {0};
|
||||
|
||||
// venc config
|
||||
static xmedia_s32 g_venc_chn = 0;
|
||||
static sample_venc_config g_venc_config = {0};
|
||||
|
||||
static int sample_h264_sys_init(int sensor_type)
|
||||
{
|
||||
xmedia_s32 ret, idx;
|
||||
xmedia_s32 blk_size = 0;
|
||||
vi_sensor_info sensor_info = {0};
|
||||
xmedia_video_size pic_size = {0};
|
||||
sample_sys_config sys_config = {0};
|
||||
|
||||
func_entry();
|
||||
|
||||
g_video_param.video_fmt = XMEDIA_VIDEO_FMT_LINEAR;
|
||||
g_video_param.pixel_fmt = XMEDIA_VIDEO_PIXEL_FMT_YVU_SEMIPLANAR_420;
|
||||
g_video_param.data_width = XMEDIA_VIDEO_DATA_WIDTH_8;
|
||||
g_video_param.compress_mode = XMEDIA_VIDEO_COMPRESS_MODE_NONE;
|
||||
|
||||
sample_comm_vi_get_sensor_info(sensor_type, &sensor_info);
|
||||
|
||||
// sys init
|
||||
sys_config.sys_conf.pipe_mode[g_vi_pipe].vicap_viproc_mode = XMEDIA_WORK_MODE_ONLINE;
|
||||
sys_config.sys_conf.pipe_mode[g_vi_pipe].viproc_vpss_mode = XMEDIA_WORK_MODE_ONLINE;
|
||||
sys_config.sys_conf.pipe_mode[g_vi_pipe].gdc_vpss_mode = XMEDIA_WORK_MODE_OFFLINE;
|
||||
|
||||
idx = 0;
|
||||
if (sys_config.sys_conf.pipe_mode[g_vi_pipe].vicap_viproc_mode == XMEDIA_WORK_MODE_OFFLINE) {
|
||||
pic_size.width = sensor_info.width;
|
||||
pic_size.height = sensor_info.height;
|
||||
|
||||
blk_size = sample_comm_sys_get_buffer_size(pic_size, g_video_param.video_fmt, sensor_info.pixel_format,
|
||||
sensor_info.bit_width, g_video_param.compress_mode);
|
||||
sys_config.vb_conf.common_pool[idx].block_size = blk_size;
|
||||
sys_config.vb_conf.common_pool[idx].block_cnt = 2;
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (sys_config.sys_conf.pipe_mode[g_vi_pipe].vicap_viproc_mode == XMEDIA_WORK_MODE_OFFLINE) {
|
||||
pic_size.width = sensor_info.width;
|
||||
pic_size.height = sensor_info.height;
|
||||
|
||||
blk_size = sample_comm_sys_get_buffer_size(pic_size, g_video_param.video_fmt, g_video_param.pixel_fmt,
|
||||
g_video_param.data_width, g_video_param.compress_mode);
|
||||
sys_config.vb_conf.common_pool[idx].block_size = blk_size;
|
||||
sys_config.vb_conf.common_pool[idx].block_cnt = 3;
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
get_user_format(NULL, &pic_size, NULL);
|
||||
|
||||
blk_size = sample_comm_sys_get_buffer_size(pic_size, g_video_param.video_fmt, g_video_param.pixel_fmt,
|
||||
g_video_param.data_width, g_video_param.compress_mode);
|
||||
sys_config.vb_conf.common_pool[idx].block_size = blk_size;
|
||||
sys_config.vb_conf.common_pool[idx].block_cnt = 3;
|
||||
|
||||
idx++;
|
||||
|
||||
sys_config.vb_conf.supplement_config = (1 << 0);
|
||||
sys_config.vb_conf.supplement_config |= (1 << 1);
|
||||
|
||||
ret = sample_comm_sys_init(&sys_config);
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("sample_comm_sys_init failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
//in online-online mode,vi and vpss must be reset at the same time
|
||||
ret = sample_comm_vi_init();
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("sample_comm_vi_init failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sample_comm_vpss_init();
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("sample_comm_vpss_init failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sample_comm_venc_init();
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("sample_comm_venc_init failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
func_success();
|
||||
|
||||
return XMEDIA_SUCCESS;
|
||||
}
|
||||
|
||||
static int sample_h264_sys_deinit(void)
|
||||
{
|
||||
sample_comm_venc_exit();
|
||||
|
||||
sample_comm_vpss_exit();
|
||||
|
||||
sample_comm_vi_exit();
|
||||
|
||||
sample_comm_sys_exit();
|
||||
|
||||
return XMEDIA_SUCCESS;
|
||||
}
|
||||
|
||||
static int sample_h264_save_data(xmedia_venc_stream* pst_stream)
|
||||
{
|
||||
int i = 0;
|
||||
xmedia_venc_pack *pst_data = NULL;
|
||||
unsigned char *s = NULL;
|
||||
unsigned int data_len = 0;
|
||||
unsigned int copy_size = 0;
|
||||
|
||||
uvc_cache_t *uvc_cache = uvc_cache_get();
|
||||
frame_node_t *fnode = NULL;
|
||||
|
||||
if (uvc_cache) {
|
||||
get_node_from_queue(uvc_cache->free_queue, &fnode);
|
||||
}
|
||||
|
||||
if (!fnode) {
|
||||
venc_logd("drop frame.\n");
|
||||
return XMEDIA_SUCCESS;
|
||||
}
|
||||
|
||||
fnode->used = 0;
|
||||
|
||||
for (i = 0; i < pst_stream->pack_count; ++i) {
|
||||
pst_data = &pst_stream->pack[i];
|
||||
s = pst_data->vir_addr + pst_data->offset;
|
||||
data_len = pst_data->len - pst_data->offset;
|
||||
copy_size = data_len < (fnode->length - fnode->used) ? data_len : (fnode->length - fnode->used);
|
||||
|
||||
if (copy_size > 0) {
|
||||
memcpy(fnode->mem + fnode->used, s, copy_size);
|
||||
fnode->used += copy_size;
|
||||
}
|
||||
|
||||
if (data_len > copy_size) {
|
||||
venc_logw("WARN: missing pack, exp(%d) != act(%d)\n", data_len, copy_size);
|
||||
}
|
||||
}
|
||||
|
||||
venc_logd("send size(%u)\n", fnode->used);
|
||||
put_node_to_queue(uvc_cache->ok_queue, fnode);
|
||||
|
||||
return XMEDIA_SUCCESS;
|
||||
}
|
||||
|
||||
static int sample_h264_save_stream(xmedia_payload_type en_type, xmedia_venc_stream *pst_stream)
|
||||
{
|
||||
xmedia_s32 ret = XMEDIA_FAILURE;
|
||||
|
||||
ret = sample_h264_save_data(pst_stream);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void* sample_h264_stream_proc(void* arg)
|
||||
{
|
||||
sample_venc_getstream_para *param = (sample_venc_getstream_para*)arg;
|
||||
xmedia_s32 chn_cnt = param->cnt;
|
||||
xmedia_s32 i;
|
||||
xmedia_s32 ret = XMEDIA_FAILURE;
|
||||
xmedia_venc_chn_attr venc_chn_attr;
|
||||
xmedia_payload_type payload_type[VENC_MAX_CHN_NUM];
|
||||
|
||||
struct timeval time_out;
|
||||
xmedia_venc_stream venc_stream;
|
||||
xmedia_venc_chn_status stat;
|
||||
xmedia_u32 venc_mask = 0;
|
||||
|
||||
if (chn_cnt >= VENC_MAX_CHN_NUM) {
|
||||
venc_loge("input venc chn count invaild\n");
|
||||
return XMEDIA_NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < chn_cnt; ++i) {
|
||||
if (param->venc_chn[i] < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = xmedia_venc_get_chn_attr(param->venc_chn[i], &venc_chn_attr);
|
||||
if (XMEDIA_SUCCESS != ret) {
|
||||
venc_loge("xmedia_venc_get_chn_attr chn[%d] failed with %#x!\n", param->venc_chn[i], ret);
|
||||
return XMEDIA_NULL;
|
||||
}
|
||||
|
||||
payload_type[i] = venc_chn_attr.venc_attr.en_type;
|
||||
venc_mask |= 1 << param->venc_chn[i];
|
||||
}
|
||||
|
||||
venc_logi("H264 start.\n");
|
||||
while (param->thread_start) {
|
||||
time_out.tv_sec = 2;
|
||||
time_out.tv_usec = 0;
|
||||
ret = xmedia_venc_select(venc_mask, &time_out);
|
||||
if (ret == XMEDIA_ERRCODE_INVALID_PARAM || ret == XMEDIA_FAILURE) {
|
||||
venc_loge("select err\n");
|
||||
break;
|
||||
} else if (ret == XMEDIA_ERRCODE_TIMEOUT) {
|
||||
param->stream_timeout_cnt++;
|
||||
venc_logd("get venc stream time out, continue. \n");
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i < chn_cnt; ++i) {
|
||||
if (param->venc_chn[i] < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = xmedia_venc_query_status(param->venc_chn[i], &stat);
|
||||
if (XMEDIA_SUCCESS != ret) {
|
||||
venc_logd("xmedia_venc_query_status chn[%d] failed with %#x!\n", i, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
if (0 == stat.cur_packs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
memset(&venc_stream, 0, sizeof(venc_stream));
|
||||
venc_stream.pack = (xmedia_venc_pack*)malloc(sizeof(xmedia_venc_pack) * stat.cur_packs);
|
||||
if (XMEDIA_NULL == venc_stream.pack) {
|
||||
venc_logd("malloc stream pack failed!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
venc_stream.pack_count = stat.cur_packs;
|
||||
ret = xmedia_venc_get_stream(param->venc_chn[i], &venc_stream, XMEDIA_TRUE);
|
||||
if (XMEDIA_SUCCESS != ret) {
|
||||
free(venc_stream.pack);
|
||||
venc_stream.pack = XMEDIA_NULL;
|
||||
xmedia_venc_release_stream(param->venc_chn[i], &venc_stream);
|
||||
venc_loge("xmedia_venc_get_stream failed with %#x!\n", ret);
|
||||
break;
|
||||
}
|
||||
|
||||
sample_h264_save_stream(payload_type[i], &venc_stream);
|
||||
|
||||
ret = xmedia_venc_release_stream(param->venc_chn[i], &venc_stream);
|
||||
if (XMEDIA_SUCCESS != ret) {
|
||||
venc_loge("xmedia_venc_release_stream failed!\n");
|
||||
free(venc_stream.pack);
|
||||
venc_stream.pack = XMEDIA_NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
free(venc_stream.pack);
|
||||
venc_stream.pack = XMEDIA_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
venc_logi("H264 stream exit.\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int sample_h264_start_get_stream(int venc_chn, int cnt)
|
||||
{
|
||||
int i, rc;
|
||||
|
||||
g_st_param.cnt = cnt;
|
||||
g_st_param.thread_start = XMEDIA_TRUE;
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
g_st_param.venc_chn[i] = venc_chn;
|
||||
}
|
||||
|
||||
rc = pthread_create(&g_recv_pid, NULL, sample_h264_stream_proc, (void*)&g_st_param);
|
||||
if (rc < 0) {
|
||||
venc_loge("Create sample_h264_stream_proc failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sample_h264_stop_get_stream(void)
|
||||
{
|
||||
if (XMEDIA_TRUE == g_st_param.thread_start) {
|
||||
g_st_param.thread_start = XMEDIA_FALSE;
|
||||
pthread_join(g_recv_pid, 0);
|
||||
}
|
||||
|
||||
venc_logi("stop get H264 stream ok.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sample_h264_normalp_classic(void)
|
||||
{
|
||||
xmedia_s32 ret = XMEDIA_SUCCESS;
|
||||
|
||||
xmedia_u32 framerate = 0, fps_user = 0;
|
||||
xmedia_video_size pic_size = {0};
|
||||
xmedia_video_size vpss_ochn_size = {0};
|
||||
xmedia_isp_config isp_config = {0};
|
||||
xmedia_payload_type en_payload = PT_H264;
|
||||
vi_sensor_info sensor_info = {0};
|
||||
|
||||
g_vi_config.dev_info[g_vi_dev].dev_en = XMEDIA_TRUE;
|
||||
g_vi_config.dev_info[g_vi_dev].dev_no = g_vi_dev;
|
||||
g_vi_config.dev_info[g_vi_dev].sensor_type = g_sensor_type;
|
||||
g_vi_config.pipe_info[g_vi_pipe].pipe_en = XMEDIA_TRUE;
|
||||
g_vi_config.pipe_info[g_vi_pipe].pipe_no = g_vi_pipe;
|
||||
g_vi_config.pipe_info[g_vi_pipe].chn_info[g_vi_chn].chn_en = XMEDIA_TRUE;
|
||||
g_vi_config.pipe_info[g_vi_pipe].chn_info[g_vi_chn].chn_no = g_vi_chn;
|
||||
g_vi_config.dev_bind_pipe[g_vi_dev].pipe[0] = g_vi_pipe;
|
||||
g_vi_config.dev_bind_pipe[g_vi_dev].pipe[1] = -1;
|
||||
|
||||
sample_comm_vi_get_sensor_info(g_sensor_type, &sensor_info);
|
||||
sample_comm_vi_get_framerate_by_sensor(g_sensor_type, &framerate);
|
||||
|
||||
get_user_format(NULL, NULL, &fps_user);
|
||||
if (fps_user > framerate) {
|
||||
venc_logw("User framerate(%d) > sensor framerate(%d), used sensor framerate\n", fps_user, framerate);
|
||||
} else {
|
||||
framerate = fps_user;
|
||||
}
|
||||
|
||||
isp_config.fps = framerate;
|
||||
isp_config.mode_config.work_mode = XMEDIA_ISP_WORK_MODE_MASTER;
|
||||
isp_config.mode_config.master_mode.blend_stat_enable = XMEDIA_FALSE;
|
||||
isp_config.mode_config.master_mode.slave_num = 0;
|
||||
isp_config.pixel_fmt = sensor_info.pixel_format;
|
||||
isp_config.size.height = sensor_info.height;
|
||||
isp_config.size.width = sensor_info.width;
|
||||
pic_size.height = sensor_info.height;
|
||||
pic_size.width = sensor_info.width;
|
||||
isp_config.wdr_mode = sensor_info.wdr_mode;
|
||||
|
||||
// isp pipe init
|
||||
g_isp_param.pipe[g_vi_pipe] = g_vi_pipe;
|
||||
g_isp_param.isp_info[g_vi_pipe].sensor_type = g_sensor_type;
|
||||
g_isp_param.isp_info[g_vi_pipe].flip = XMEDIA_FALSE;
|
||||
g_isp_param.isp_info[g_vi_pipe].mirror = XMEDIA_FALSE;
|
||||
g_isp_param.isp_info[g_vi_pipe].isp_pipe_en = XMEDIA_TRUE;
|
||||
g_isp_param.isp_info[g_vi_pipe].isp_sensor_en = XMEDIA_TRUE;
|
||||
|
||||
memcpy(&(g_isp_param.isp_info[g_vi_pipe].isp_config), &isp_config, sizeof(xmedia_isp_config));
|
||||
|
||||
// isp init
|
||||
ret = sample_comm_isp_init(&g_isp_param, &g_vi_config);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("isp init failed!\n");
|
||||
goto exit0;
|
||||
}
|
||||
|
||||
// vi start
|
||||
ret = sample_comm_vi_start(&g_vi_config, &g_video_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("start vi failed!\n");
|
||||
goto exit1;
|
||||
}
|
||||
|
||||
// isp start
|
||||
ret = sample_comm_isp_start(&g_isp_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("start isp failed!\n");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
// vpss config
|
||||
ret = sample_comm_vpss_get_default_pipe_cfg(&g_vpss_config.pipe_info[g_vpss_pipe].pipe_config, pic_size, &g_video_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("get default pipe cfg failed !\n");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
get_user_format(&en_payload, &pic_size, NULL);
|
||||
vpss_ochn_size.width = pic_size.width;
|
||||
vpss_ochn_size.height = pic_size.height;
|
||||
|
||||
g_vpss_config.pipe_info[g_vpss_pipe].pipe_en = XMEDIA_TRUE;
|
||||
g_vpss_config.pipe_info[g_vpss_pipe].pipe_no = g_vpss_pipe;
|
||||
|
||||
g_vpss_config.pipe_info[g_vpss_pipe].chn_info[g_vpss_ochn].chn_en = XMEDIA_TRUE;
|
||||
g_vpss_config.pipe_info[g_vpss_pipe].chn_info[g_vpss_ochn].chn_no = g_vpss_ochn;
|
||||
ret = sample_comm_vpss_get_default_ochn_cfg(&g_vpss_config.pipe_info[g_vpss_pipe].chn_info[g_vpss_ochn].chn_config,
|
||||
vpss_ochn_size, &g_video_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("get default pipe cfg failed !\n");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
// vpss start
|
||||
ret = sample_comm_vpss_start(&g_vpss_config);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("vpss start failed !\n");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
ret = sample_comm_sys_vi_bind_vpss(g_vi_pipe, g_vi_chn, g_vpss_pipe, g_vpss_ichn);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("vi bind vpss failed !\n");
|
||||
goto exit3;
|
||||
}
|
||||
|
||||
//venc init
|
||||
g_venc_config.chn_info[g_venc_chn].venc_en = XMEDIA_TRUE;
|
||||
g_venc_config.chn_info[g_venc_chn].venc_chn = g_venc_chn;
|
||||
g_venc_config.chn_info[g_venc_chn].payload_type = en_payload;
|
||||
|
||||
if (en_payload == PT_H264)
|
||||
g_venc_config.chn_info[g_venc_chn].rc_mode = VENC_RC_MODE_H264CBR;
|
||||
else if (en_payload == PT_H265)
|
||||
g_venc_config.chn_info[g_venc_chn].rc_mode = VENC_RC_MODE_H265CBR;
|
||||
|
||||
sample_comm_venc_get_default_chn_info(vpss_ochn_size, framerate, &g_venc_config.chn_info[g_venc_chn]);
|
||||
|
||||
ret = sample_comm_venc_start(&g_venc_config);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("venc start failed !\n");
|
||||
goto exit4;
|
||||
}
|
||||
|
||||
ret = sample_comm_sys_vpss_bind_venc(g_vpss_pipe, g_vpss_ochn, g_venc_chn);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("vpss bind venc failed !\n");
|
||||
goto exit5;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
sample_comm_sys_vpss_unbind_venc(g_vpss_pipe, g_vpss_ochn, g_venc_chn);
|
||||
exit5:
|
||||
sample_comm_venc_stop(&g_venc_config);
|
||||
exit4:
|
||||
sample_comm_sys_vi_unbind_vpss(g_vi_pipe, g_vi_chn, g_vpss_pipe, g_vpss_ichn);
|
||||
exit3:
|
||||
sample_comm_vpss_stop(&g_vpss_config);
|
||||
exit2:
|
||||
sample_comm_isp_stop(&g_isp_param);
|
||||
exit1:
|
||||
sample_comm_vi_stop(&g_vi_config);
|
||||
exit0:
|
||||
sample_comm_isp_exit(&g_isp_param);
|
||||
|
||||
sample_h264_sys_deinit();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* Processing Unit Operation Functions
|
||||
*******************************************************/
|
||||
|
||||
/* Processing Unit Operation Functions End */
|
||||
|
||||
/*******************************************************
|
||||
* Input Terminal Operation Functions
|
||||
*******************************************************/
|
||||
|
||||
/* Input Terminal Operation Functions End */
|
||||
|
||||
/*******************************************************
|
||||
* Stream Control Operation Functions
|
||||
*******************************************************/
|
||||
int sample_h264_set_idr(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_h264_init(void)
|
||||
{
|
||||
g_sensor_type = SENSOR0_TYPE;
|
||||
sample_h264_sys_init(g_sensor_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_h264_startup(void)
|
||||
{
|
||||
venc_logi("H264 stream startup.\n");
|
||||
g_start = 1;
|
||||
|
||||
if (sample_h264_normalp_classic() < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
sample_h264_start_get_stream(g_venc_chn, 1);
|
||||
venc_logi("H264 stream startup ok.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_h264_shutdown(void)
|
||||
{
|
||||
if (!g_start) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sample_h264_stop_get_stream();
|
||||
|
||||
sample_comm_sys_vpss_unbind_venc(g_vpss_pipe, g_vpss_ochn, g_venc_chn);
|
||||
|
||||
sample_comm_venc_stop(&g_venc_config);
|
||||
|
||||
sample_comm_sys_vi_unbind_vpss(g_vi_pipe, g_vi_chn, g_vpss_pipe, g_vpss_ichn);
|
||||
|
||||
sample_comm_vpss_stop(&g_vpss_config);
|
||||
|
||||
sample_comm_isp_stop(&g_isp_param);
|
||||
|
||||
sample_comm_vi_stop(&g_vi_config);
|
||||
|
||||
sample_comm_isp_exit(&g_isp_param);
|
||||
|
||||
sample_h264_sys_deinit();
|
||||
|
||||
g_start = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __SAMPLE_VIDEO_H264_H__
|
||||
#define __SAMPLE_VIDEO_H264_H__
|
||||
|
||||
int sample_h264_set_idr(void);
|
||||
int sample_h264_init(void);
|
||||
int sample_h264_startup(void);
|
||||
int sample_h264_shutdown(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,559 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "sample_comm.h"
|
||||
#include "sample_comm_venc.h"
|
||||
#include "sample_comm_vpss.h"
|
||||
#include "sample_comm_vi.h"
|
||||
#include "sample_comm_sys.h"
|
||||
#include "sample_comm_isp.h"
|
||||
|
||||
#include "video_stream.h"
|
||||
#include "sample_video.h"
|
||||
#include "sample_video_mjpeg.h"
|
||||
|
||||
#include "venc_log.h"
|
||||
#include "frame_cache.h"
|
||||
|
||||
static int g_start = 0;
|
||||
static pthread_t g_recv_pid;
|
||||
static sample_venc_getstream_para g_st_param;
|
||||
|
||||
//common
|
||||
static sample_comm_sensor_type g_sensor_type = SENSOR0_TYPE;
|
||||
static sample_comm_video_param g_video_param = {0};
|
||||
|
||||
// vi config
|
||||
static xmedia_s32 g_vi_dev = 0; /* 0:单板SENSOR0; 2:单板SENSOR2 */
|
||||
static xmedia_s32 g_vi_chn = 0;
|
||||
static xmedia_s32 g_vi_pipe = 0;
|
||||
static sample_vi_config g_vi_config = {0};
|
||||
|
||||
// isp config
|
||||
static sample_isp_param g_isp_param = {0};
|
||||
|
||||
// vpss config
|
||||
static xmedia_s32 g_vpss_pipe = 0;
|
||||
static xmedia_s32 g_vpss_ichn = 0;
|
||||
static xmedia_s32 g_vpss_ochn = 0;
|
||||
static sample_vpss_config g_vpss_config = {0};
|
||||
|
||||
// venc config
|
||||
static xmedia_s32 g_venc_chn = 0;
|
||||
static sample_venc_config g_venc_config = {0};
|
||||
|
||||
static int sample_mjpeg_sys_init(int sensor_type)
|
||||
{
|
||||
xmedia_s32 ret, idx;
|
||||
xmedia_s32 blk_size = 0;
|
||||
vi_sensor_info sensor_info = {0};
|
||||
xmedia_video_size pic_size = {0};
|
||||
sample_sys_config sys_config = {0};
|
||||
|
||||
func_entry();
|
||||
|
||||
g_video_param.video_fmt = XMEDIA_VIDEO_FMT_LINEAR;
|
||||
g_video_param.pixel_fmt = XMEDIA_VIDEO_PIXEL_FMT_YVU_SEMIPLANAR_420;
|
||||
g_video_param.data_width = XMEDIA_VIDEO_DATA_WIDTH_8;
|
||||
g_video_param.compress_mode = XMEDIA_VIDEO_COMPRESS_MODE_NONE;
|
||||
|
||||
sample_comm_vi_get_sensor_info(sensor_type, &sensor_info);
|
||||
|
||||
// sys init
|
||||
sys_config.sys_conf.pipe_mode[g_vi_pipe].vicap_viproc_mode = XMEDIA_WORK_MODE_ONLINE;
|
||||
sys_config.sys_conf.pipe_mode[g_vi_pipe].viproc_vpss_mode = XMEDIA_WORK_MODE_ONLINE;
|
||||
sys_config.sys_conf.pipe_mode[g_vi_pipe].gdc_vpss_mode = XMEDIA_WORK_MODE_OFFLINE;
|
||||
|
||||
sys_config.vb_conf.max_pool_cnt = 25;
|
||||
|
||||
idx = 0;
|
||||
|
||||
if (sys_config.sys_conf.pipe_mode[g_vi_pipe].vicap_viproc_mode == XMEDIA_WORK_MODE_OFFLINE) {
|
||||
pic_size.width = sensor_info.width;
|
||||
pic_size.height = sensor_info.height;
|
||||
|
||||
blk_size = sample_comm_sys_get_buffer_size(pic_size, g_video_param.video_fmt, sensor_info.pixel_format,
|
||||
sensor_info.bit_width, g_video_param.compress_mode);
|
||||
sys_config.vb_conf.common_pool[idx].block_size = blk_size;
|
||||
sys_config.vb_conf.common_pool[idx].block_cnt = 2;
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (sys_config.sys_conf.pipe_mode[g_vi_pipe].viproc_vpss_mode == XMEDIA_WORK_MODE_OFFLINE) {
|
||||
pic_size.width = sensor_info.width;
|
||||
pic_size.height = sensor_info.height;
|
||||
|
||||
blk_size = sample_comm_sys_get_buffer_size(pic_size, g_video_param.video_fmt, g_video_param.pixel_fmt,
|
||||
g_video_param.data_width, g_video_param.compress_mode);
|
||||
sys_config.vb_conf.common_pool[idx].block_size = blk_size;
|
||||
sys_config.vb_conf.common_pool[idx].block_cnt = 3;
|
||||
idx++;
|
||||
}
|
||||
|
||||
get_user_format(NULL, &pic_size, NULL);
|
||||
venc_logi("user resolution: %d(w) x %d(h)\n", pic_size.width, pic_size.height);
|
||||
blk_size = sample_comm_sys_get_buffer_size(pic_size, g_video_param.video_fmt, g_video_param.pixel_fmt,
|
||||
g_video_param.data_width, g_video_param.compress_mode);
|
||||
sys_config.vb_conf.common_pool[idx].block_size = blk_size;
|
||||
sys_config.vb_conf.common_pool[idx].block_cnt = 3;
|
||||
|
||||
sys_config.vb_conf.supplement_config = (1 << 0);
|
||||
sys_config.vb_conf.supplement_config |= (1 << 1);
|
||||
|
||||
ret = sample_comm_sys_init(&sys_config);
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("sample_comm_sys_init failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
//in online-online mode,vi and vpss must be reset at the same time
|
||||
ret = sample_comm_vi_init();
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("sample_comm_vi_init failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sample_comm_vpss_init();
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("sample_comm_vpss_init failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sample_comm_venc_init();
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("sample_comm_venc_init failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
func_success();
|
||||
|
||||
return XMEDIA_SUCCESS;
|
||||
}
|
||||
|
||||
static int sample_mjpeg_sys_deinit(void)
|
||||
{
|
||||
sample_comm_venc_exit();
|
||||
|
||||
sample_comm_vpss_exit();
|
||||
|
||||
sample_comm_vi_exit();
|
||||
|
||||
sample_comm_sys_exit();
|
||||
|
||||
return XMEDIA_SUCCESS;
|
||||
}
|
||||
|
||||
static int sample_mjpeg_save_data(xmedia_venc_stream* pst_stream)
|
||||
{
|
||||
int i = 0;
|
||||
xmedia_venc_pack *pst_data = NULL;
|
||||
unsigned char *s = NULL;
|
||||
unsigned int data_len = 0;
|
||||
unsigned int copy_size = 0;
|
||||
|
||||
uvc_cache_t *uvc_cache = uvc_cache_get();
|
||||
frame_node_t *fnode = NULL;
|
||||
|
||||
if (uvc_cache) {
|
||||
get_node_from_queue(uvc_cache->free_queue, &fnode);
|
||||
}
|
||||
|
||||
if (!fnode) {
|
||||
venc_logd("drop frame.\n");
|
||||
return XMEDIA_SUCCESS;
|
||||
}
|
||||
|
||||
fnode->used = 0;
|
||||
|
||||
for (i = 0; i < pst_stream->pack_count; ++i) {
|
||||
pst_data = &pst_stream->pack[i];
|
||||
s = pst_data->vir_addr + pst_data->offset;
|
||||
data_len = pst_data->len - pst_data->offset;
|
||||
copy_size = data_len < (fnode->length - fnode->used) ? data_len : (fnode->length - fnode->used);
|
||||
|
||||
if (copy_size > 0) {
|
||||
memcpy(fnode->mem + fnode->used, s, copy_size);
|
||||
fnode->used += copy_size;
|
||||
}
|
||||
|
||||
if (data_len > copy_size) {
|
||||
venc_logw("WARN: missing pack, exp(%d) != act(%d)\n", data_len, copy_size);
|
||||
}
|
||||
}
|
||||
|
||||
venc_logd("send size(%u)\n", fnode->used);
|
||||
put_node_to_queue(uvc_cache->ok_queue, fnode);
|
||||
|
||||
return XMEDIA_SUCCESS;
|
||||
}
|
||||
|
||||
static int sample_mjpeg_save_stream(xmedia_payload_type en_type, xmedia_venc_stream *pst_stream)
|
||||
{
|
||||
xmedia_s32 ret = XMEDIA_FAILURE;
|
||||
|
||||
if (PT_MJPEG == en_type) {
|
||||
ret = sample_mjpeg_save_data(pst_stream);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void* sample_mjpeg_stream_proc(void* arg)
|
||||
{
|
||||
sample_venc_getstream_para *param = (sample_venc_getstream_para*)arg;
|
||||
xmedia_s32 chn_cnt = param->cnt;
|
||||
xmedia_s32 i;
|
||||
xmedia_s32 ret = XMEDIA_FAILURE;
|
||||
xmedia_venc_chn_attr venc_chn_attr;
|
||||
xmedia_payload_type payload_type[VENC_MAX_CHN_NUM];
|
||||
|
||||
struct timeval time_out;
|
||||
xmedia_venc_stream venc_stream;
|
||||
xmedia_venc_chn_status stat;
|
||||
xmedia_u32 venc_mask = 0;
|
||||
|
||||
if (chn_cnt >= VENC_MAX_CHN_NUM) {
|
||||
venc_loge("input venc chn count invaild\n");
|
||||
return XMEDIA_NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < chn_cnt; ++i) {
|
||||
if (param->venc_chn[i] < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = xmedia_venc_get_chn_attr(param->venc_chn[i], &venc_chn_attr);
|
||||
if (XMEDIA_SUCCESS != ret) {
|
||||
venc_loge("xmedia_venc_get_chn_attr chn[%d] failed with %#x!\n", param->venc_chn[i], ret);
|
||||
return XMEDIA_NULL;
|
||||
}
|
||||
|
||||
payload_type[i] = venc_chn_attr.venc_attr.en_type;
|
||||
venc_mask |= 1 << param->venc_chn[i];
|
||||
}
|
||||
|
||||
venc_logi("mjpeg start.\n");
|
||||
while (param->thread_start) {
|
||||
time_out.tv_sec = 2;
|
||||
time_out.tv_usec = 0;
|
||||
ret = xmedia_venc_select(venc_mask, &time_out);
|
||||
if (ret == XMEDIA_ERRCODE_INVALID_PARAM || ret == XMEDIA_FAILURE) {
|
||||
venc_loge("select err\n");
|
||||
break;
|
||||
} else if (ret == XMEDIA_ERRCODE_TIMEOUT) {
|
||||
param->stream_timeout_cnt++;
|
||||
venc_logd("get venc stream time out, continue. \n");
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i < chn_cnt; ++i) {
|
||||
if (param->venc_chn[i] < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = xmedia_venc_query_status(param->venc_chn[i], &stat);
|
||||
if (XMEDIA_SUCCESS != ret) {
|
||||
venc_logd("xmedia_venc_query_status chn[%d] failed with %#x!\n", i, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
if (0 == stat.cur_packs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
memset(&venc_stream, 0, sizeof(venc_stream));
|
||||
venc_stream.pack = (xmedia_venc_pack*)malloc(sizeof(xmedia_venc_pack) * stat.cur_packs);
|
||||
if (XMEDIA_NULL == venc_stream.pack) {
|
||||
venc_logd("malloc stream pack failed!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
venc_stream.pack_count = stat.cur_packs;
|
||||
ret = xmedia_venc_get_stream(param->venc_chn[i], &venc_stream, XMEDIA_TRUE);
|
||||
if (XMEDIA_SUCCESS != ret) {
|
||||
free(venc_stream.pack);
|
||||
venc_stream.pack = XMEDIA_NULL;
|
||||
xmedia_venc_release_stream(param->venc_chn[i], &venc_stream);
|
||||
venc_loge("xmedia_venc_get_stream failed with %#x!\n", ret);
|
||||
break;
|
||||
}
|
||||
|
||||
sample_mjpeg_save_stream(payload_type[i], &venc_stream);
|
||||
|
||||
ret = xmedia_venc_release_stream(param->venc_chn[i], &venc_stream);
|
||||
if (XMEDIA_SUCCESS != ret) {
|
||||
venc_loge("xmedia_venc_release_stream failed!\n");
|
||||
free(venc_stream.pack);
|
||||
venc_stream.pack = XMEDIA_NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
free(venc_stream.pack);
|
||||
venc_stream.pack = XMEDIA_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
venc_logi("mjpeg stream exit.\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int sample_mjpeg_start_get_stream(int venc_chn, int cnt)
|
||||
{
|
||||
int i, rc;
|
||||
|
||||
g_st_param.cnt = cnt;
|
||||
g_st_param.thread_start = XMEDIA_TRUE;
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
g_st_param.venc_chn[i] = venc_chn;
|
||||
}
|
||||
|
||||
rc = pthread_create(&g_recv_pid, NULL, sample_mjpeg_stream_proc, (void*)&g_st_param);
|
||||
if (rc < 0) {
|
||||
venc_loge("Create sample_mjpeg_stream_proc failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sample_mjpeg_stop_get_stream(void)
|
||||
{
|
||||
if (XMEDIA_TRUE == g_st_param.thread_start) {
|
||||
g_st_param.thread_start = XMEDIA_FALSE;
|
||||
pthread_join(g_recv_pid, 0);
|
||||
}
|
||||
|
||||
venc_logi("stop get mjpeg stream ok.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sample_mjpeg_normalp_classic(void)
|
||||
{
|
||||
xmedia_s32 ret = XMEDIA_SUCCESS;
|
||||
|
||||
xmedia_u32 framerate = 0, fps_user = 0;
|
||||
xmedia_video_size pic_size = {0};
|
||||
xmedia_video_size vpss_ochn_size = {0};
|
||||
xmedia_isp_config isp_config = {0};
|
||||
xmedia_payload_type en_payload = PT_MJPEG;
|
||||
vi_sensor_info sensor_info = {0};
|
||||
|
||||
g_vi_config.dev_info[g_vi_dev].dev_en = XMEDIA_TRUE;
|
||||
g_vi_config.dev_info[g_vi_dev].dev_no = g_vi_dev;
|
||||
g_vi_config.dev_info[g_vi_dev].sensor_type = g_sensor_type;
|
||||
g_vi_config.pipe_info[g_vi_pipe].pipe_en = XMEDIA_TRUE;
|
||||
g_vi_config.pipe_info[g_vi_pipe].pipe_no = g_vi_pipe;
|
||||
g_vi_config.pipe_info[g_vi_pipe].chn_info[g_vi_chn].chn_en = XMEDIA_TRUE;
|
||||
g_vi_config.pipe_info[g_vi_pipe].chn_info[g_vi_chn].chn_no = g_vi_chn;
|
||||
g_vi_config.dev_bind_pipe[g_vi_dev].pipe[0] = g_vi_pipe;
|
||||
g_vi_config.dev_bind_pipe[g_vi_dev].pipe[1] = -1;
|
||||
|
||||
sample_comm_vi_get_sensor_info(g_sensor_type, &sensor_info);
|
||||
sample_comm_vi_get_framerate_by_sensor(g_sensor_type, &framerate);
|
||||
|
||||
get_user_format(NULL, NULL, &fps_user);
|
||||
if (fps_user > framerate) {
|
||||
venc_logw("User framerate(%d) > sensor framerate(%d), used sensor framerate\n", fps_user, framerate);
|
||||
} else {
|
||||
framerate = fps_user;
|
||||
}
|
||||
|
||||
isp_config.fps = framerate;
|
||||
isp_config.mode_config.work_mode = XMEDIA_ISP_WORK_MODE_MASTER;
|
||||
isp_config.mode_config.master_mode.blend_stat_enable = XMEDIA_FALSE;
|
||||
isp_config.mode_config.master_mode.slave_num = 0;
|
||||
isp_config.pixel_fmt = sensor_info.pixel_format;
|
||||
isp_config.size.height = sensor_info.height;
|
||||
isp_config.size.width = sensor_info.width;
|
||||
pic_size.height = sensor_info.height;
|
||||
pic_size.width = sensor_info.width;
|
||||
isp_config.wdr_mode = sensor_info.wdr_mode;
|
||||
|
||||
// isp pipe init
|
||||
g_isp_param.pipe[g_vi_pipe] = g_vi_pipe;
|
||||
g_isp_param.isp_info[g_vi_pipe].sensor_type = g_sensor_type;
|
||||
g_isp_param.isp_info[g_vi_pipe].flip = XMEDIA_FALSE;
|
||||
g_isp_param.isp_info[g_vi_pipe].mirror = XMEDIA_FALSE;
|
||||
g_isp_param.isp_info[g_vi_pipe].isp_pipe_en = XMEDIA_TRUE;
|
||||
g_isp_param.isp_info[g_vi_pipe].isp_sensor_en = XMEDIA_TRUE;
|
||||
|
||||
memcpy(&(g_isp_param.isp_info[g_vi_pipe].isp_config), &isp_config, sizeof(xmedia_isp_config));
|
||||
|
||||
// isp init
|
||||
ret = sample_comm_isp_init(&g_isp_param, &g_vi_config);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("isp init failed!\n");
|
||||
goto exit0;
|
||||
}
|
||||
|
||||
// vi start
|
||||
ret = sample_comm_vi_start(&g_vi_config, &g_video_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("start vi failed!\n");
|
||||
goto exit1;
|
||||
}
|
||||
|
||||
// isp start
|
||||
ret = sample_comm_isp_start(&g_isp_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("start isp failed!\n");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
// vpss config
|
||||
ret = sample_comm_vpss_get_default_pipe_cfg(&g_vpss_config.pipe_info[g_vpss_pipe].pipe_config, pic_size, &g_video_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("get default pipe cfg failed !\n");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
get_user_format(&en_payload, &pic_size, NULL);
|
||||
vpss_ochn_size.width = pic_size.width;
|
||||
vpss_ochn_size.height = pic_size.height;
|
||||
|
||||
g_vpss_config.pipe_info[g_vpss_pipe].pipe_en = XMEDIA_TRUE;
|
||||
g_vpss_config.pipe_info[g_vpss_pipe].pipe_no = g_vpss_pipe;
|
||||
|
||||
g_vpss_config.pipe_info[g_vpss_pipe].chn_info[g_vpss_ochn].chn_en = XMEDIA_TRUE;
|
||||
g_vpss_config.pipe_info[g_vpss_pipe].chn_info[g_vpss_ochn].chn_no = g_vpss_ochn;
|
||||
ret = sample_comm_vpss_get_default_ochn_cfg(&g_vpss_config.pipe_info[g_vpss_pipe].chn_info[g_vpss_ochn].chn_config,
|
||||
vpss_ochn_size, &g_video_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("get default pipe cfg failed !\n");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
// vpss start
|
||||
ret = sample_comm_vpss_start(&g_vpss_config);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("vpss start failed !\n");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
ret = sample_comm_sys_vi_bind_vpss(g_vi_pipe, g_vi_chn, g_vpss_pipe, g_vpss_ichn);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("vi bind vpss failed !\n");
|
||||
goto exit3;
|
||||
}
|
||||
|
||||
//venc init
|
||||
g_venc_config.chn_info[g_venc_chn].venc_en = XMEDIA_TRUE;
|
||||
g_venc_config.chn_info[g_venc_chn].venc_chn = g_venc_chn;
|
||||
g_venc_config.chn_info[g_venc_chn].payload_type = en_payload;
|
||||
g_venc_config.chn_info[g_venc_chn].rc_mode = VENC_RC_MODE_MJPEGFIXQP;
|
||||
//g_venc_config.chn_info[g_venc_chn].rc_mode = VENC_RC_MODE_MJPEGCBR;
|
||||
sample_comm_venc_get_default_chn_info(vpss_ochn_size, framerate, &g_venc_config.chn_info[g_venc_chn]);
|
||||
|
||||
ret = sample_comm_venc_start(&g_venc_config);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("venc start failed !\n");
|
||||
goto exit4;
|
||||
}
|
||||
|
||||
ret = sample_comm_sys_vpss_bind_venc(g_vpss_pipe, g_vpss_ochn, g_venc_chn);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("vpss bind venc failed !\n");
|
||||
goto exit5;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
sample_comm_sys_vpss_unbind_venc(g_vpss_pipe, g_vpss_ochn, g_venc_chn);
|
||||
exit5:
|
||||
sample_comm_venc_stop(&g_venc_config);
|
||||
exit4:
|
||||
sample_comm_sys_vi_unbind_vpss(g_vi_pipe, g_vi_chn, g_vpss_pipe, g_vpss_ichn);
|
||||
exit3:
|
||||
sample_comm_vpss_stop(&g_vpss_config);
|
||||
exit2:
|
||||
sample_comm_isp_stop(&g_isp_param);
|
||||
exit1:
|
||||
sample_comm_vi_stop(&g_vi_config);
|
||||
exit0:
|
||||
sample_comm_isp_exit(&g_isp_param);
|
||||
|
||||
sample_mjpeg_sys_deinit();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* Processing Unit Operation Functions
|
||||
*******************************************************/
|
||||
|
||||
/* Processing Unit Operation Functions End */
|
||||
|
||||
/*******************************************************
|
||||
* Input Terminal Operation Functions
|
||||
*******************************************************/
|
||||
|
||||
/* Input Terminal Operation Functions End */
|
||||
|
||||
/*******************************************************
|
||||
* Stream Control Operation Functions
|
||||
*******************************************************/
|
||||
int sample_mjpeg_set_idr(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_mjpeg_init(void)
|
||||
{
|
||||
g_sensor_type = SENSOR0_TYPE;
|
||||
sample_mjpeg_sys_init(g_sensor_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_mjpeg_startup(void)
|
||||
{
|
||||
venc_logi("Mjpeg stream startup.\n");
|
||||
g_start = 1;
|
||||
|
||||
if (sample_mjpeg_normalp_classic() < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
sample_mjpeg_start_get_stream(g_venc_chn, 1);
|
||||
venc_logi("Mjpeg stream startup ok.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_mjpeg_shutdown(void)
|
||||
{
|
||||
if (!g_start) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sample_mjpeg_stop_get_stream();
|
||||
|
||||
sample_comm_sys_vpss_unbind_venc(g_vpss_pipe, g_vpss_ochn, g_venc_chn);
|
||||
|
||||
sample_comm_venc_stop(&g_venc_config);
|
||||
|
||||
sample_comm_sys_vi_unbind_vpss(g_vi_pipe, g_vi_chn, g_vpss_pipe, g_vpss_ichn);
|
||||
|
||||
sample_comm_vpss_stop(&g_vpss_config);
|
||||
|
||||
sample_comm_isp_stop(&g_isp_param);
|
||||
|
||||
sample_comm_vi_stop(&g_vi_config);
|
||||
|
||||
sample_comm_isp_exit(&g_isp_param);
|
||||
|
||||
sample_mjpeg_sys_deinit();
|
||||
|
||||
g_start = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __SAMPLE_VIDEO_MJPEG_H__
|
||||
#define __SAMPLE_VIDEO_MJPEG_H__
|
||||
|
||||
int sample_mjpeg_set_idr(void);
|
||||
int sample_mjpeg_init(void);
|
||||
int sample_mjpeg_startup(void);
|
||||
int sample_mjpeg_shutdown(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,655 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "sample_comm.h"
|
||||
#include "sample_comm_venc.h"
|
||||
#include "sample_comm_vpss.h"
|
||||
#include "sample_comm_vi.h"
|
||||
#include "sample_comm_sys.h"
|
||||
#include "sample_comm_isp.h"
|
||||
|
||||
#include "video_stream.h"
|
||||
#include "sample_video.h"
|
||||
#include "sample_video_mjpeg.h"
|
||||
|
||||
#include "venc_log.h"
|
||||
#include "frame_cache.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
extern unsigned int pack_h264_to_jpeg(unsigned char* frame_jpeg, unsigned int size_jpeg, unsigned int len_jpeg,
|
||||
unsigned char* frame_h264, unsigned int size_h264, unsigned int fps);
|
||||
|
||||
static int g_start = 0;
|
||||
static pthread_t g_recv_pid;
|
||||
static sample_venc_getstream_para g_st_param;
|
||||
|
||||
//common
|
||||
static sample_comm_sensor_type g_sensor_type = SENSOR0_TYPE;
|
||||
static sample_comm_video_param g_video_param = {0};
|
||||
|
||||
// vi config
|
||||
static xmedia_s32 g_vi_dev = 0; /* 0:单板SENSOR0; 2:单板SENSOR2 */
|
||||
static xmedia_s32 g_vi_chn = 0;
|
||||
static xmedia_s32 g_vi_pipe = 0;
|
||||
static sample_vi_config g_vi_config = {0};
|
||||
|
||||
// isp config
|
||||
static sample_isp_param g_isp_param = {0};
|
||||
|
||||
// vpss config
|
||||
static xmedia_s32 g_vpss_pipe = 0;
|
||||
static xmedia_s32 g_vpss_ichn = 0;
|
||||
static xmedia_s32 g_vpss_ochn_0 = 0;
|
||||
static xmedia_s32 g_vpss_ochn_1 = 1;
|
||||
static sample_vpss_config g_vpss_config = {0};
|
||||
|
||||
// venc config
|
||||
static xmedia_s32 g_venc_chn_0 = 0;
|
||||
static xmedia_s32 g_venc_chn_1 = 1;
|
||||
static sample_venc_config g_venc_config = {0};
|
||||
|
||||
static int sample_mjpeg_h264_sys_init(int sensor_type)
|
||||
{
|
||||
xmedia_s32 ret, idx;
|
||||
xmedia_s32 blk_size = 0;
|
||||
vi_sensor_info sensor_info = {0};
|
||||
xmedia_video_size pic_size = {0};
|
||||
sample_sys_config sys_config = {0};
|
||||
|
||||
func_entry();
|
||||
|
||||
g_video_param.video_fmt = XMEDIA_VIDEO_FMT_LINEAR;
|
||||
g_video_param.pixel_fmt = XMEDIA_VIDEO_PIXEL_FMT_YVU_SEMIPLANAR_420;
|
||||
g_video_param.data_width = XMEDIA_VIDEO_DATA_WIDTH_8;
|
||||
g_video_param.compress_mode = XMEDIA_VIDEO_COMPRESS_MODE_NONE;
|
||||
|
||||
sample_comm_vi_get_sensor_info(sensor_type, &sensor_info);
|
||||
|
||||
// sys init
|
||||
sys_config.sys_conf.pipe_mode[g_vi_pipe].vicap_viproc_mode = XMEDIA_WORK_MODE_ONLINE;
|
||||
sys_config.sys_conf.pipe_mode[g_vi_pipe].viproc_vpss_mode = XMEDIA_WORK_MODE_ONLINE;
|
||||
sys_config.sys_conf.pipe_mode[g_vi_pipe].gdc_vpss_mode = XMEDIA_WORK_MODE_OFFLINE;
|
||||
|
||||
idx = 0;
|
||||
|
||||
if (sys_config.sys_conf.pipe_mode[g_vi_pipe].vicap_viproc_mode == XMEDIA_WORK_MODE_OFFLINE) {
|
||||
pic_size.width = sensor_info.width;
|
||||
pic_size.height = sensor_info.height;
|
||||
|
||||
blk_size = sample_comm_sys_get_buffer_size(pic_size, g_video_param.video_fmt, sensor_info.pixel_format,
|
||||
sensor_info.bit_width, g_video_param.compress_mode);
|
||||
sys_config.vb_conf.common_pool[idx].block_size = blk_size;
|
||||
sys_config.vb_conf.common_pool[idx].block_cnt = 2;
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (sys_config.sys_conf.pipe_mode[g_vi_pipe].viproc_vpss_mode == XMEDIA_WORK_MODE_OFFLINE) {
|
||||
pic_size.width = sensor_info.width;
|
||||
pic_size.height = sensor_info.height;
|
||||
|
||||
blk_size = sample_comm_sys_get_buffer_size(pic_size, g_video_param.video_fmt, g_video_param.pixel_fmt,
|
||||
g_video_param.data_width, g_video_param.compress_mode);
|
||||
sys_config.vb_conf.common_pool[idx].block_size = blk_size;
|
||||
sys_config.vb_conf.common_pool[idx].block_cnt = 3;
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
get_user_format(NULL, &pic_size, NULL);
|
||||
venc_logi("user resolution: %d(w) x %d(h)\n", pic_size.width, pic_size.height);
|
||||
blk_size = sample_comm_sys_get_buffer_size(pic_size, g_video_param.video_fmt, g_video_param.pixel_fmt,
|
||||
g_video_param.data_width, g_video_param.compress_mode);
|
||||
sys_config.vb_conf.common_pool[idx].block_size = blk_size;
|
||||
sys_config.vb_conf.common_pool[idx].block_cnt = 6;
|
||||
|
||||
sys_config.vb_conf.supplement_config = 1;
|
||||
|
||||
ret = sample_comm_sys_init(&sys_config);
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("sample_comm_sys_init failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
//in online-online mode,vi and vpss must be reset at the same time
|
||||
ret = sample_comm_vi_init();
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("sample_comm_vi_init failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sample_comm_vpss_init();
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("sample_comm_vpss_init failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sample_comm_venc_init();
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("sample_comm_venc_init failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
func_success();
|
||||
|
||||
return XMEDIA_SUCCESS;
|
||||
}
|
||||
|
||||
static int sample_mjpeg_h264_sys_deinit(void)
|
||||
{
|
||||
sample_comm_venc_exit();
|
||||
|
||||
sample_comm_vpss_exit();
|
||||
|
||||
sample_comm_vi_exit();
|
||||
|
||||
sample_comm_sys_exit();
|
||||
|
||||
return XMEDIA_SUCCESS;
|
||||
}
|
||||
|
||||
extern void memcpy_jpeg_user(unsigned char* src_buffer, unsigned int src_len,
|
||||
unsigned char* dest_buffer, unsigned int dest_length, unsigned int* dest_offset);
|
||||
|
||||
static int sample_copy_frame(xmedia_venc_stream* pst_stream, char* dst_buf, int buf_len, int buf_used, int type)
|
||||
{
|
||||
int i = 0;
|
||||
xmedia_venc_pack *pst_data = NULL;
|
||||
unsigned char *s = NULL;
|
||||
unsigned int data_len = 0;
|
||||
unsigned int copy_size = 0;
|
||||
|
||||
for (i = 0; i < pst_stream->pack_count; ++i) {
|
||||
pst_data = &pst_stream->pack[i];
|
||||
s = pst_data->vir_addr + pst_data->offset;
|
||||
data_len = pst_data->len - pst_data->offset;
|
||||
copy_size = data_len < (buf_len - buf_used) ? data_len : (buf_len - buf_used);
|
||||
|
||||
if (copy_size > 0) {
|
||||
if (type == PT_MJPEG) {
|
||||
memcpy_jpeg_user(s, data_len, (unsigned char*)dst_buf, buf_len, (unsigned int *)&buf_used);
|
||||
} else {
|
||||
memcpy(dst_buf + buf_used, s, copy_size);
|
||||
buf_used += copy_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (data_len > copy_size) {
|
||||
venc_logw("WARN: missing pack.\n");
|
||||
}
|
||||
}
|
||||
|
||||
return buf_used;
|
||||
}
|
||||
|
||||
static char* g_h264 = NULL;
|
||||
#define H264_BUFF_LEN 0x80000
|
||||
|
||||
extern unsigned int pack_h264_to_jpeg(unsigned char* frame_jpeg, unsigned int size_jpeg, unsigned int len_jpeg,
|
||||
unsigned char* frame_h264, unsigned int size_h264, unsigned int fps);
|
||||
|
||||
static void sample_save_h264(xmedia_venc_stream* pst_stream, frame_node_t* fnode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (fnode->used == 0) {
|
||||
venc_loge("Not found mjpeg frame, drop current frame\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memset(g_h264, 0, H264_BUFF_LEN);
|
||||
ret = sample_copy_frame(pst_stream, g_h264, H264_BUFF_LEN, 0, PT_H264);
|
||||
|
||||
venc_logd("h264_size = %d, fnode->used = %u, fnode->length = %d\n", ret, fnode->used, fnode->length);
|
||||
|
||||
debug_dump_frame((char*)g_h264, ret, "frame.h264");
|
||||
debug_dump_frame((char*)fnode->mem, fnode->used, "frame.mjpeg");
|
||||
ret = pack_h264_to_jpeg((unsigned char*)fnode->mem, fnode->used, fnode->length,
|
||||
(unsigned char*)g_h264, ret, 30);
|
||||
|
||||
debug_dump_frame((char*)fnode->mem, ret, "frame_mjpeg_h264h.bin");
|
||||
fnode->used = ret;
|
||||
}
|
||||
|
||||
static void sample_save_mjpeg(xmedia_venc_stream* pst_stream, frame_node_t* fnode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sample_copy_frame(pst_stream, (char*)fnode->mem, fnode->length, fnode->used, PT_MJPEG);
|
||||
|
||||
fnode->used = ret;
|
||||
}
|
||||
|
||||
static void sample_mjpeg_h264_save_stream(xmedia_payload_type en_type, xmedia_venc_stream *pst_stream, frame_node_t* fnode)
|
||||
{
|
||||
if (PT_MJPEG == en_type) {
|
||||
sample_save_mjpeg(pst_stream, fnode);
|
||||
} else if (PT_H264 == en_type){
|
||||
sample_save_h264(pst_stream, fnode);
|
||||
}
|
||||
}
|
||||
|
||||
void* sample_mjpeg_h264_stream_proc(void* arg)
|
||||
{
|
||||
sample_venc_getstream_para *param = (sample_venc_getstream_para*)arg;
|
||||
xmedia_s32 chn_cnt = param->cnt;
|
||||
xmedia_s32 i;
|
||||
xmedia_s32 ret = XMEDIA_FAILURE;
|
||||
xmedia_venc_chn_attr venc_chn_attr;
|
||||
xmedia_payload_type payload_type[VENC_MAX_CHN_NUM];
|
||||
|
||||
struct timeval time_out;
|
||||
void *venc_pack = NULL;
|
||||
xmedia_venc_stream venc_stream;
|
||||
xmedia_venc_chn_status stat;
|
||||
xmedia_u32 venc_mask = 0;
|
||||
|
||||
uvc_cache_t *uvc_cache = uvc_cache_get();
|
||||
frame_node_t *fnode = NULL;
|
||||
|
||||
if (chn_cnt >= VENC_MAX_CHN_NUM) {
|
||||
venc_loge("input venc chn count invaild\n");
|
||||
return XMEDIA_NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < chn_cnt; ++i) {
|
||||
if (param->venc_chn[i] < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = xmedia_venc_get_chn_attr(param->venc_chn[i], &venc_chn_attr);
|
||||
if (XMEDIA_SUCCESS != ret) {
|
||||
venc_loge("xmedia_venc_get_chn_attr chn[%d] failed with %#x!\n", param->venc_chn[i], ret);
|
||||
return XMEDIA_NULL;
|
||||
}
|
||||
|
||||
payload_type[i] = venc_chn_attr.venc_attr.en_type;
|
||||
venc_mask |= 1 << param->venc_chn[i];
|
||||
}
|
||||
|
||||
venc_pack = malloc(sizeof(xmedia_venc_pack) * 5);
|
||||
if (venc_pack == NULL) {
|
||||
venc_loge("Failed to malloc venc pack\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_h264 = (char*)malloc(H264_BUFF_LEN);
|
||||
if (g_h264 == NULL) {
|
||||
venc_loge("Failed to malloc\n");
|
||||
free(venc_pack);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
venc_logi("mjpeg and h264 start.\n");
|
||||
while (param->thread_start) {
|
||||
time_out.tv_sec = 2;
|
||||
time_out.tv_usec = 0;
|
||||
ret = xmedia_venc_select(venc_mask, &time_out);
|
||||
if (ret == XMEDIA_ERRCODE_INVALID_PARAM || ret == XMEDIA_FAILURE) {
|
||||
venc_loge("select err\n");
|
||||
break;
|
||||
} else if (ret == XMEDIA_ERRCODE_TIMEOUT) {
|
||||
param->stream_timeout_cnt++;
|
||||
venc_logd("get venc stream time out, continue. \n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (uvc_cache) {
|
||||
get_node_from_queue(uvc_cache->free_queue, &fnode);
|
||||
}
|
||||
|
||||
if (!fnode) {
|
||||
//venc_logw("drop frame.\n");
|
||||
} else {
|
||||
fnode->used = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < chn_cnt; ++i) {
|
||||
if (param->venc_chn[i] < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = xmedia_venc_query_status(param->venc_chn[i], &stat);
|
||||
if (XMEDIA_SUCCESS != ret) {
|
||||
venc_logd("xmedia_venc_query_status chn[%d] failed with %#x!\n", i, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
if (0 == stat.cur_packs) {
|
||||
break;
|
||||
}
|
||||
|
||||
memset(&venc_stream, 0, sizeof(venc_stream));
|
||||
venc_stream.pack = (xmedia_venc_pack*)venc_pack;
|
||||
if (XMEDIA_NULL == venc_stream.pack) {
|
||||
venc_logd("malloc stream pack failed!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
venc_stream.pack_count = stat.cur_packs;
|
||||
ret = xmedia_venc_get_stream(param->venc_chn[i], &venc_stream, XMEDIA_TRUE);
|
||||
if (XMEDIA_SUCCESS != ret) {
|
||||
venc_stream.pack = XMEDIA_NULL;
|
||||
xmedia_venc_release_stream(param->venc_chn[i], &venc_stream);
|
||||
venc_loge("xmedia_venc_get_stream failed with %#x!\n", ret);
|
||||
break;
|
||||
}
|
||||
|
||||
if (fnode != NULL) {
|
||||
sample_mjpeg_h264_save_stream(payload_type[i], &venc_stream, fnode);
|
||||
}
|
||||
|
||||
ret = xmedia_venc_release_stream(param->venc_chn[i], &venc_stream);
|
||||
if (XMEDIA_SUCCESS != ret) {
|
||||
venc_loge("xmedia_venc_release_stream failed!\n");
|
||||
venc_stream.pack = XMEDIA_NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
venc_stream.pack = XMEDIA_NULL;
|
||||
}
|
||||
|
||||
if (fnode != NULL) {
|
||||
venc_logd("send size(%u)\n", fnode->used);
|
||||
if (fnode->used) {
|
||||
put_node_to_queue(uvc_cache->ok_queue, fnode);
|
||||
} else {
|
||||
put_node_to_queue(uvc_cache->free_queue, fnode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(venc_pack);
|
||||
free(g_h264);
|
||||
venc_logi("mjpeg and stream exit.\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int sample_mjpeg_h264_start_get_stream(void)
|
||||
{
|
||||
int i, rc, cnt;
|
||||
|
||||
cnt = 2;
|
||||
g_st_param.cnt = cnt;
|
||||
g_st_param.thread_start = XMEDIA_TRUE;
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
g_st_param.venc_chn[i] = i; // g_venc_chn_0, g_venc_chn_1
|
||||
}
|
||||
|
||||
rc = pthread_create(&g_recv_pid, NULL, sample_mjpeg_h264_stream_proc, (void*)&g_st_param);
|
||||
if (rc < 0) {
|
||||
venc_loge("Create sample_mjpeg_h264_stream_proc failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sample_mjpeg_h264_stop_get_stream(void)
|
||||
{
|
||||
if (XMEDIA_TRUE == g_st_param.thread_start) {
|
||||
g_st_param.thread_start = XMEDIA_FALSE;
|
||||
pthread_join(g_recv_pid, 0);
|
||||
}
|
||||
|
||||
venc_logi("stop get mjpeg stream ok.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sample_mjpeg_h264_normalp_classic(void)
|
||||
{
|
||||
xmedia_s32 ret = XMEDIA_SUCCESS;
|
||||
|
||||
xmedia_u32 framerate = 0, fps_user = 0;
|
||||
xmedia_video_size pic_size = {0};
|
||||
xmedia_video_size vpss_ochn_size = {0};
|
||||
xmedia_isp_config isp_config = {0};
|
||||
xmedia_payload_type en_payload = PT_MJPEG;
|
||||
vi_sensor_info sensor_info = {0};
|
||||
|
||||
g_vi_config.dev_info[g_vi_dev].dev_en = XMEDIA_TRUE;
|
||||
g_vi_config.dev_info[g_vi_dev].dev_no = g_vi_dev;
|
||||
g_vi_config.dev_info[g_vi_dev].sensor_type = g_sensor_type;
|
||||
g_vi_config.pipe_info[g_vi_pipe].pipe_en = XMEDIA_TRUE;
|
||||
g_vi_config.pipe_info[g_vi_pipe].pipe_no = g_vi_pipe;
|
||||
g_vi_config.pipe_info[g_vi_pipe].chn_info[g_vi_chn].chn_en = XMEDIA_TRUE;
|
||||
g_vi_config.pipe_info[g_vi_pipe].chn_info[g_vi_chn].chn_no = g_vi_chn;
|
||||
g_vi_config.dev_bind_pipe[g_vi_dev].pipe[0] = g_vi_pipe;
|
||||
g_vi_config.dev_bind_pipe[g_vi_dev].pipe[1] = -1;
|
||||
|
||||
sample_comm_vi_get_sensor_info(g_sensor_type, &sensor_info);
|
||||
sample_comm_vi_get_framerate_by_sensor(g_sensor_type, &framerate);
|
||||
|
||||
get_user_format(NULL, NULL, &fps_user);
|
||||
if (fps_user > framerate) {
|
||||
venc_logw("User framerate(%d) > sensor framerate(%d), used sensor framerate\n", fps_user, framerate);
|
||||
} else {
|
||||
framerate = fps_user;
|
||||
}
|
||||
|
||||
isp_config.fps = framerate;
|
||||
isp_config.mode_config.work_mode = XMEDIA_ISP_WORK_MODE_MASTER;
|
||||
isp_config.mode_config.master_mode.blend_stat_enable = XMEDIA_FALSE;
|
||||
isp_config.mode_config.master_mode.slave_num = 0;
|
||||
isp_config.pixel_fmt = sensor_info.pixel_format;
|
||||
isp_config.size.height = sensor_info.height;
|
||||
isp_config.size.width = sensor_info.width;
|
||||
pic_size.height = sensor_info.height;
|
||||
pic_size.width = sensor_info.width;
|
||||
isp_config.wdr_mode = sensor_info.wdr_mode;
|
||||
|
||||
// isp pipe init
|
||||
g_isp_param.pipe[g_vi_pipe] = g_vi_pipe;
|
||||
g_isp_param.isp_info[g_vi_pipe].sensor_type = g_sensor_type;
|
||||
g_isp_param.isp_info[g_vi_pipe].flip = XMEDIA_FALSE;
|
||||
g_isp_param.isp_info[g_vi_pipe].mirror = XMEDIA_FALSE;
|
||||
g_isp_param.isp_info[g_vi_pipe].isp_pipe_en = XMEDIA_TRUE;
|
||||
g_isp_param.isp_info[g_vi_pipe].isp_sensor_en = XMEDIA_TRUE;
|
||||
|
||||
memcpy(&(g_isp_param.isp_info[g_vi_pipe].isp_config), &isp_config, sizeof(xmedia_isp_config));
|
||||
|
||||
// isp init
|
||||
ret = sample_comm_isp_init(&g_isp_param, &g_vi_config);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("isp init failed!\n");
|
||||
goto exit0;
|
||||
}
|
||||
|
||||
// vi start
|
||||
ret = sample_comm_vi_start(&g_vi_config, &g_video_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("start vi failed!\n");
|
||||
goto exit1;
|
||||
}
|
||||
|
||||
// isp start
|
||||
ret = sample_comm_isp_start(&g_isp_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("start isp failed!\n");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
// vpss config
|
||||
ret = sample_comm_vpss_get_default_pipe_cfg(&g_vpss_config.pipe_info[g_vpss_pipe].pipe_config, pic_size, &g_video_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("get default pipe cfg failed !\n");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
get_user_format(&en_payload, &pic_size, NULL);
|
||||
vpss_ochn_size.width = pic_size.width;
|
||||
vpss_ochn_size.height = pic_size.height;
|
||||
|
||||
g_vpss_config.pipe_info[g_vpss_pipe].pipe_en = XMEDIA_TRUE;
|
||||
g_vpss_config.pipe_info[g_vpss_pipe].pipe_no = g_vpss_pipe;
|
||||
|
||||
// vpss out channel 0
|
||||
g_vpss_config.pipe_info[g_vpss_pipe].chn_info[g_vpss_ochn_0].chn_en = XMEDIA_TRUE;
|
||||
g_vpss_config.pipe_info[g_vpss_pipe].chn_info[g_vpss_ochn_0].chn_no = g_vpss_ochn_0;
|
||||
ret = sample_comm_vpss_get_default_ochn_cfg(&g_vpss_config.pipe_info[g_vpss_pipe].chn_info[g_vpss_ochn_0].chn_config,
|
||||
vpss_ochn_size, &g_video_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("get default pipe cfg failed !\n");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
// vpss out channel 1
|
||||
g_vpss_config.pipe_info[g_vpss_pipe].chn_info[g_vpss_ochn_1].chn_en = XMEDIA_TRUE;
|
||||
g_vpss_config.pipe_info[g_vpss_pipe].chn_info[g_vpss_ochn_1].chn_no = g_vpss_ochn_1;
|
||||
ret = sample_comm_vpss_get_default_ochn_cfg(&g_vpss_config.pipe_info[g_vpss_pipe].chn_info[g_vpss_ochn_1].chn_config,
|
||||
vpss_ochn_size, &g_video_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("get default pipe cfg failed !\n");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
// vpss start
|
||||
ret = sample_comm_vpss_start(&g_vpss_config);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("vpss start failed !\n");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
ret = sample_comm_sys_vi_bind_vpss(g_vi_pipe, g_vi_chn, g_vpss_pipe, g_vpss_ichn);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("vi bind vpss failed !\n");
|
||||
goto exit3;
|
||||
}
|
||||
|
||||
//venc channel 0 init
|
||||
g_venc_config.chn_info[g_venc_chn_0].venc_en = XMEDIA_TRUE;
|
||||
g_venc_config.chn_info[g_venc_chn_0].venc_chn = g_venc_chn_0;
|
||||
g_venc_config.chn_info[g_venc_chn_0].payload_type = PT_MJPEG;
|
||||
//g_venc_config.chn_info[g_venc_chn_0].rc_mode = VENC_RC_MODE_MJPEGFIXQP;
|
||||
g_venc_config.chn_info[g_venc_chn_0].rc_mode = VENC_RC_MODE_MJPEGCBR;
|
||||
sample_comm_venc_get_default_chn_info(vpss_ochn_size, framerate, &g_venc_config.chn_info[g_venc_chn_0]);
|
||||
|
||||
//venc channel 1 init
|
||||
g_venc_config.chn_info[g_venc_chn_1].venc_en = XMEDIA_TRUE;
|
||||
g_venc_config.chn_info[g_venc_chn_1].venc_chn = g_venc_chn_1;
|
||||
g_venc_config.chn_info[g_venc_chn_1].payload_type = PT_H264;
|
||||
g_venc_config.chn_info[g_venc_chn_1].rc_mode = VENC_RC_MODE_H264CBR;
|
||||
sample_comm_venc_get_default_chn_info(vpss_ochn_size, framerate, &g_venc_config.chn_info[g_venc_chn_1]);
|
||||
|
||||
ret = sample_comm_venc_start(&g_venc_config);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("venc start failed !\n");
|
||||
goto exit4;
|
||||
}
|
||||
|
||||
ret = sample_comm_sys_vpss_bind_venc(g_vpss_pipe, g_vpss_ochn_0, g_venc_chn_0);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("vpss bind venc failed !\n");
|
||||
goto exit5;
|
||||
}
|
||||
|
||||
ret = sample_comm_sys_vpss_bind_venc(g_vpss_pipe, g_vpss_ochn_1, g_venc_chn_1);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("vpss bind venc failed !\n");
|
||||
goto exit5;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
sample_comm_sys_vpss_unbind_venc(g_vpss_pipe, g_vpss_ochn_0, g_venc_chn_0);
|
||||
sample_comm_sys_vpss_unbind_venc(g_vpss_pipe, g_vpss_ochn_1, g_venc_chn_1);
|
||||
exit5:
|
||||
sample_comm_venc_stop(&g_venc_config);
|
||||
exit4:
|
||||
sample_comm_sys_vi_unbind_vpss(g_vi_pipe, g_vi_chn, g_vpss_pipe, g_vpss_ichn);
|
||||
exit3:
|
||||
sample_comm_vpss_stop(&g_vpss_config);
|
||||
exit2:
|
||||
sample_comm_isp_stop(&g_isp_param);
|
||||
exit1:
|
||||
sample_comm_vi_stop(&g_vi_config);
|
||||
exit0:
|
||||
sample_comm_isp_exit(&g_isp_param);
|
||||
|
||||
sample_mjpeg_h264_sys_deinit();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* Processing Unit Operation Functions
|
||||
*******************************************************/
|
||||
|
||||
/* Processing Unit Operation Functions End */
|
||||
|
||||
/*******************************************************
|
||||
* Input Terminal Operation Functions
|
||||
*******************************************************/
|
||||
|
||||
/* Input Terminal Operation Functions End */
|
||||
|
||||
/*******************************************************
|
||||
* Stream Control Operation Functions
|
||||
*******************************************************/
|
||||
int sample_mjpeg_h264_set_idr(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_mjpeg_h264_init(void)
|
||||
{
|
||||
g_sensor_type = SENSOR0_TYPE;
|
||||
sample_mjpeg_h264_sys_init(g_sensor_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_mjpeg_h264_startup(void)
|
||||
{
|
||||
venc_logi("Mjpeg stream startup.\n");
|
||||
g_start = 1;
|
||||
|
||||
if (sample_mjpeg_h264_normalp_classic() < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
sample_mjpeg_h264_start_get_stream();
|
||||
venc_logi("Mjpeg stream startup ok.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_mjpeg_h264_shutdown(void)
|
||||
{
|
||||
if (!g_start) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sample_mjpeg_h264_stop_get_stream();
|
||||
|
||||
sample_comm_sys_vpss_unbind_venc(g_vpss_pipe, g_vpss_ochn_0, g_venc_chn_0);
|
||||
|
||||
sample_comm_sys_vpss_unbind_venc(g_vpss_pipe, g_vpss_ochn_1, g_venc_chn_1);
|
||||
|
||||
sample_comm_venc_stop(&g_venc_config);
|
||||
|
||||
sample_comm_sys_vi_unbind_vpss(g_vi_pipe, g_vi_chn, g_vpss_pipe, g_vpss_ichn);
|
||||
|
||||
sample_comm_vpss_stop(&g_vpss_config);
|
||||
|
||||
sample_comm_isp_stop(&g_isp_param);
|
||||
|
||||
sample_comm_vi_stop(&g_vi_config);
|
||||
|
||||
sample_comm_isp_exit(&g_isp_param);
|
||||
|
||||
sample_mjpeg_h264_sys_deinit();
|
||||
|
||||
g_start = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __SAMPLE_VIDEO_MJPEG_H264_H__
|
||||
#define __SAMPLE_VIDEO_MJPEG_H264_H__
|
||||
|
||||
int sample_mjpeg_h264_set_idr(void);
|
||||
int sample_mjpeg_h264_init(void);
|
||||
int sample_mjpeg_h264_startup(void);
|
||||
int sample_mjpeg_h264_shutdown(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,842 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "sample_comm.h"
|
||||
#include "sample_comm_vpss.h"
|
||||
#include "sample_comm_vi.h"
|
||||
#include "sample_comm_sys.h"
|
||||
#include "sample_comm_isp.h"
|
||||
|
||||
#include "xmedia_sys.h"
|
||||
#include "xmedia_mmz.h"
|
||||
#include "xmedia_vb.h"
|
||||
#include "xmedia_vpss.h"
|
||||
#include "xmedia_vgs.h"
|
||||
|
||||
#include "video_stream.h"
|
||||
#include "sample_video.h"
|
||||
#include "sample_video_yuv.h"
|
||||
|
||||
#include "venc_log.h"
|
||||
#include "frame_cache.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define v4l2_fourcc(a, b, c, d) \
|
||||
((uint32_t)(a) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
|
||||
|
||||
#define V4L2_PIX_FMT_YUYV v4l2_fourcc('Y', 'U', 'Y', 'V') /* 16 YUV 4:2:2 */
|
||||
#define V4L2_PIX_FMT_NV21 v4l2_fourcc('N', 'V', '2', '1') /* 12 Y/CrCb 4:2:0 */
|
||||
#define V4L2_PIX_FMT_NV12 v4l2_fourcc('N', 'V', '1', '2') /* 12 Y/CbCr 4:2:0 */
|
||||
#define V4L2_PIX_FMT_YUV420 v4l2_fourcc('Y', 'U', '1', '2') /* 12 YUV 4:2:0 */
|
||||
#define V4L2_PIX_FMT_YVU420 v4l2_fourcc('Y', 'V', '1', '2') /* 12 YUV 4:2:0 */
|
||||
|
||||
static int g_tag_fmt = V4L2_PIX_FMT_YUYV;
|
||||
|
||||
static pthread_t g_recv_pid;
|
||||
static int g_start = 0;
|
||||
static int g_recv = 0;
|
||||
static xmedia_u32 g_vpss_depth_flag = 0;
|
||||
//static xmedia_u32 g_vpss_ori_depth = 0;
|
||||
|
||||
//common
|
||||
static sample_comm_sensor_type g_sensor_type = SENSOR0_TYPE;
|
||||
static sample_comm_video_param g_video_param = {0};
|
||||
|
||||
// vi config
|
||||
static xmedia_s32 g_vi_dev = 0; /* 0:单板SENSOR0; 2:单板SENSOR2 */
|
||||
static xmedia_s32 g_vi_chn = 0;
|
||||
static xmedia_s32 g_vi_pipe = 0;
|
||||
static xmedia_u32 g_vgs_pool_id = 0;
|
||||
static sample_vi_config g_vi_config = {0};
|
||||
|
||||
// isp config
|
||||
static sample_isp_param g_isp_param = {0};
|
||||
|
||||
// vpss config
|
||||
static xmedia_s32 g_vpss_pipe = 0;
|
||||
static xmedia_s32 g_vpss_ichn = 0;
|
||||
static xmedia_s32 g_vpss_ochn = 0;
|
||||
static sample_vpss_config g_vpss_config = {0};
|
||||
static xmedia_video_frame_info g_vpss_frame_info = {0};
|
||||
|
||||
static int sample_yuv_create_yuyv_pool(void)
|
||||
{
|
||||
xmedia_vb_pool_config pool_config;
|
||||
vi_sensor_info sensor_info = {0};
|
||||
|
||||
sample_comm_vi_get_sensor_info(g_sensor_type, &sensor_info);
|
||||
|
||||
pool_config.block_cnt = 1;
|
||||
pool_config.block_size = sensor_info.width * sensor_info.height * 2;
|
||||
pool_config.map_mode = XMEDIA_VB_MAP_MODE_NONE;
|
||||
g_vgs_pool_id = xmedia_vb_create_pool(&pool_config);
|
||||
if (g_vgs_pool_id == -1) {
|
||||
venc_logd("resolution: w(%d) x h(%d).\n", sensor_info.width, sensor_info.height);
|
||||
return XMEDIA_FAILURE;
|
||||
}
|
||||
|
||||
return XMEDIA_SUCCESS;
|
||||
}
|
||||
|
||||
static int vgs_nv12_to_yuyv(xmedia_video_frame_info* vpss_frame, xmedia_video_frame_info* scale_frame)
|
||||
{
|
||||
xmedia_s32 ret;
|
||||
xmedia_vgs_frame_info task_info;
|
||||
xmedia_s32 job_handle;
|
||||
xmedia_handle vb_handle;
|
||||
vi_sensor_info sensor_info = {0};
|
||||
xmedia_vb_base_info base_info = {0};
|
||||
xmedia_vb_cal_cfg vb_cal_cfg = {0};
|
||||
|
||||
sample_comm_vi_get_sensor_info(g_sensor_type, &sensor_info);
|
||||
|
||||
base_info.width = vpss_frame->frame.width;
|
||||
base_info.height = vpss_frame->frame.height;
|
||||
base_info.pixel_fmt = XMEDIA_VIDEO_PIXEL_FMT_YUYV_PACKAGE_422; //XMEDIA_VIDEO_PIXEL_FMT_YVU_SEMIPLANAR_422;
|
||||
base_info.bit_width = vpss_frame->frame.bit_width;
|
||||
base_info.cmp_mode = vpss_frame->frame.compress_mode;
|
||||
base_info.align = 8;
|
||||
xmedia_vb_get_buffer_config(&base_info, &vb_cal_cfg);
|
||||
|
||||
vb_handle = xmedia_vb_get_block(g_vgs_pool_id, sensor_info.width * sensor_info.height * 2, XMEDIA_NULL);
|
||||
if (vb_handle == VB_INVALID_HANDLE) {
|
||||
SAMPLE_PRT("xmedia_vb_get_block(size:%d) failed!\n", sensor_info.width * sensor_info.height * 2);
|
||||
return XMEDIA_FAILURE;
|
||||
}
|
||||
|
||||
ret = xmedia_vgs_init();
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("vgs init failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = xmedia_vgs_create_job(&job_handle);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("create vgs job failed\n");
|
||||
xmedia_vgs_exit();
|
||||
goto EXIT1;
|
||||
}
|
||||
|
||||
memset(scale_frame, 0, sizeof(xmedia_video_frame_info));
|
||||
scale_frame->pool_id = g_vgs_pool_id;
|
||||
scale_frame->frame.width = vpss_frame->frame.width;
|
||||
scale_frame->frame.height = vpss_frame->frame.height;
|
||||
scale_frame->frame.dynamic_range = XMEDIA_VIDEO_DYNAMIC_RANGE_SDR;
|
||||
scale_frame->frame.color_info.color_gamut = XMEDIA_VIDEO_COLOR_GAMUT_BT709;
|
||||
scale_frame->frame.pixel_fmt = XMEDIA_VIDEO_PIXEL_FMT_YUYV_PACKAGE_422; //XMEDIA_VIDEO_PIXEL_FMT_YVU_SEMIPLANAR_422; XMEDIA_VIDEO_PIXEL_FMT_YUYV_PACKAGE_422
|
||||
scale_frame->frame.pts = vpss_frame->frame.pts;
|
||||
scale_frame->frame.compress_mode = vpss_frame->frame.compress_mode;
|
||||
scale_frame->frame.video_fmt = vpss_frame->frame.video_fmt;
|
||||
scale_frame->frame.bit_width = vpss_frame->frame.bit_width;
|
||||
scale_frame->mod_id = MOD_ID_USER;
|
||||
scale_frame->frame.addr.y_head_phy_addr = xmedia_vb_handle_to_phy_addr(vb_handle);
|
||||
scale_frame->frame.addr.c_head_phy_addr = scale_frame->frame.addr.y_head_phy_addr + vb_cal_cfg.head_y_size;
|
||||
scale_frame->frame.stride.y_head_stride = vb_cal_cfg.head_y_stride;
|
||||
scale_frame->frame.stride.c_head_stride = vb_cal_cfg.head_c_stride;
|
||||
scale_frame->frame.addr.y_phy_addr = scale_frame->frame.addr.y_head_phy_addr + vb_cal_cfg.head_size;
|
||||
scale_frame->frame.addr.c_phy_addr = scale_frame->frame.addr.y_head_phy_addr + vb_cal_cfg.main_y_size;
|
||||
scale_frame->frame.stride.y_stride = vb_cal_cfg.main_stride;
|
||||
scale_frame->frame.stride.c_stride = vb_cal_cfg.main_stride;
|
||||
|
||||
memcpy(&task_info.img_in, vpss_frame, sizeof(xmedia_video_frame_info));
|
||||
memcpy(&task_info.img_out, scale_frame, sizeof(xmedia_video_frame_info));
|
||||
ret = xmedia_vgs_add_task_scale(job_handle, &task_info, XMEDIA_VIDEO_SCALE_MODE_NORMAL);
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
xmedia_vgs_cancel_job(job_handle);
|
||||
SAMPLE_PRT("xmedia_vgs_add_task_scale failed!\n");
|
||||
goto EXIT2;
|
||||
}
|
||||
|
||||
ret = xmedia_vgs_submit_job(job_handle);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("submit vgs job failed\n");
|
||||
goto EXIT2;
|
||||
}
|
||||
|
||||
ret = xmedia_vgs_wait_job(job_handle, 2000);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("wait vgs job failed\n");
|
||||
goto EXIT1;
|
||||
}
|
||||
|
||||
xmedia_vgs_exit();
|
||||
|
||||
return XMEDIA_SUCCESS;
|
||||
|
||||
EXIT2:
|
||||
xmedia_vgs_cancel_job(job_handle);
|
||||
EXIT1:
|
||||
xmedia_vgs_exit();
|
||||
|
||||
return XMEDIA_FAILURE;
|
||||
}
|
||||
|
||||
static int sample_yuv_sys_init(int sensor_type)
|
||||
{
|
||||
xmedia_s32 ret, idx;
|
||||
xmedia_s32 blk_size = 0;
|
||||
vi_sensor_info sensor_info = {0};
|
||||
xmedia_video_size pic_size = {0};
|
||||
sample_sys_config sys_config = {0};
|
||||
|
||||
func_entry();
|
||||
|
||||
g_video_param.video_fmt = XMEDIA_VIDEO_FMT_LINEAR;
|
||||
g_video_param.pixel_fmt = XMEDIA_VIDEO_PIXEL_FMT_YVU_SEMIPLANAR_420; /* NV21 */
|
||||
g_video_param.data_width = XMEDIA_VIDEO_DATA_WIDTH_8;
|
||||
g_video_param.compress_mode = XMEDIA_VIDEO_COMPRESS_MODE_NONE;
|
||||
|
||||
sample_comm_vi_get_sensor_info(sensor_type, &sensor_info);
|
||||
|
||||
// sys init
|
||||
sys_config.sys_conf.pipe_mode[g_vi_pipe].vicap_viproc_mode = XMEDIA_WORK_MODE_ONLINE;
|
||||
sys_config.sys_conf.pipe_mode[g_vi_pipe].viproc_vpss_mode = XMEDIA_WORK_MODE_OFFLINE;
|
||||
sys_config.sys_conf.pipe_mode[g_vi_pipe].gdc_vpss_mode = XMEDIA_WORK_MODE_OFFLINE;
|
||||
|
||||
sys_config.vb_conf.max_pool_cnt = 25;
|
||||
|
||||
pic_size.width = sensor_info.width;
|
||||
pic_size.height = sensor_info.height;
|
||||
|
||||
venc_logd("sensor resolution: w(%d) x h(%d).\n", pic_size.width, pic_size.height);
|
||||
|
||||
idx = 0;
|
||||
|
||||
if (sys_config.sys_conf.pipe_mode[g_vi_pipe].vicap_viproc_mode == XMEDIA_WORK_MODE_OFFLINE) {
|
||||
blk_size = sample_comm_sys_get_buffer_size(pic_size, g_video_param.video_fmt, sensor_info.pixel_format,
|
||||
sensor_info.bit_width, g_video_param.compress_mode);
|
||||
sys_config.vb_conf.common_pool[idx].block_size = blk_size;
|
||||
sys_config.vb_conf.common_pool[idx].block_cnt = 2;
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (sys_config.sys_conf.pipe_mode[g_vi_pipe].viproc_vpss_mode == XMEDIA_WORK_MODE_OFFLINE) {
|
||||
blk_size = sample_comm_sys_get_buffer_size(pic_size, g_video_param.video_fmt, g_video_param.pixel_fmt,
|
||||
g_video_param.data_width, g_video_param.compress_mode);
|
||||
sys_config.vb_conf.common_pool[idx].block_size = blk_size;
|
||||
sys_config.vb_conf.common_pool[idx].block_cnt = 3;
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
get_user_format(NULL, &pic_size, NULL);
|
||||
venc_logi("user resolution: %d(w) x %d(h)\n", pic_size.width, pic_size.height);
|
||||
blk_size = sample_comm_sys_get_buffer_size(pic_size, g_video_param.video_fmt, g_video_param.pixel_fmt,
|
||||
g_video_param.data_width, g_video_param.compress_mode);
|
||||
sys_config.vb_conf.common_pool[idx].block_size = blk_size;
|
||||
sys_config.vb_conf.common_pool[idx].block_cnt = 3;
|
||||
|
||||
sys_config.vb_conf.supplement_config = 1;
|
||||
|
||||
ret = sample_comm_sys_init(&sys_config);
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("sample_comm_sys_init failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
//in online-online mode,vi and vpss must be reset at the same time
|
||||
ret = sample_comm_vi_init();
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("sample_comm_vi_init failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sample_comm_vpss_init();
|
||||
if(ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("sample_comm_vpss_init failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
func_success();
|
||||
|
||||
return XMEDIA_SUCCESS;
|
||||
}
|
||||
|
||||
static int sample_yuv_sys_deinit(void)
|
||||
{
|
||||
sample_comm_vpss_exit();
|
||||
|
||||
sample_comm_vi_exit();
|
||||
|
||||
sample_comm_sys_exit();
|
||||
|
||||
return XMEDIA_SUCCESS;
|
||||
}
|
||||
|
||||
static xmedia_s32 vpss_ochn_dump_transform_format(xmedia_video_frame_info *frame_in,
|
||||
xmedia_video_frame_info *tmp_frame, xmedia_video_frame_info *frame_out)
|
||||
{
|
||||
xmedia_s32 ret;
|
||||
xmedia_vgs_frame_info task_info;
|
||||
xmedia_s32 job_handle;
|
||||
|
||||
ret = xmedia_vgs_init();
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("vgs init failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = xmedia_vgs_create_job(&job_handle);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("create vgs job failed\n");
|
||||
xmedia_vgs_exit();
|
||||
goto EXIT1;
|
||||
}
|
||||
|
||||
if (frame_in->frame.video_fmt == XMEDIA_VIDEO_FMT_TILE_16x4) {
|
||||
memcpy(&task_info.img_in, frame_in, sizeof(xmedia_video_frame_info));
|
||||
memcpy(&task_info.img_out, tmp_frame, sizeof(xmedia_video_frame_info));
|
||||
ret = xmedia_vgs_add_task_rotation(job_handle, &task_info, XMEDIA_VIDEO_ROTATION_0);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("add vgs task failed\n");
|
||||
goto EXIT2;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(&task_info.img_in, tmp_frame, sizeof(xmedia_video_frame_info));
|
||||
memcpy(&task_info.img_out, frame_out, sizeof(xmedia_video_frame_info));
|
||||
ret = xmedia_vgs_add_task_rotation(job_handle, &task_info, XMEDIA_VIDEO_ROTATION_0);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("add vgs task failed\n");
|
||||
goto EXIT2;
|
||||
}
|
||||
|
||||
ret = xmedia_vgs_submit_job(job_handle);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("submit vgs job failed\n");
|
||||
goto EXIT2;
|
||||
}
|
||||
|
||||
ret = xmedia_vgs_wait_job(job_handle, 2000);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("wait vgs job failed\n");
|
||||
goto EXIT1;
|
||||
}
|
||||
|
||||
xmedia_vgs_exit();
|
||||
|
||||
return XMEDIA_SUCCESS;
|
||||
|
||||
EXIT2:
|
||||
xmedia_vgs_cancel_job(job_handle);
|
||||
EXIT1:
|
||||
xmedia_vgs_exit();
|
||||
|
||||
return XMEDIA_FAILURE;
|
||||
}
|
||||
|
||||
static int nv21_to_nv21(unsigned char* src_nv21, unsigned int width, unsigned int height,
|
||||
unsigned char* nv21, unsigned int nv21_len)
|
||||
{
|
||||
int copy_size;
|
||||
|
||||
copy_size = width * height * 3 / 2;
|
||||
if (copy_size > nv21_len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(nv21, src_nv21, copy_size);
|
||||
|
||||
return copy_size;
|
||||
}
|
||||
|
||||
static int nv21_to_yuyv(unsigned char* nv21, unsigned int width, unsigned int height,
|
||||
unsigned char* yuyv, unsigned int yuyv_len)
|
||||
{
|
||||
int copy_size;
|
||||
|
||||
copy_size = width * height * 2;
|
||||
if (copy_size > yuyv_len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(yuyv, nv21, copy_size);
|
||||
|
||||
return copy_size;
|
||||
}
|
||||
|
||||
static void sample_yuv_save_data(unsigned char* yuv, unsigned int src_fmt,
|
||||
unsigned int width, unsigned int height)
|
||||
{
|
||||
unsigned int copy_size = 0;
|
||||
uvc_cache_t *uvc_cache = uvc_cache_get();
|
||||
frame_node_t *fnode = NULL;
|
||||
|
||||
if (uvc_cache) {
|
||||
get_node_from_queue(uvc_cache->free_queue, &fnode);
|
||||
}
|
||||
|
||||
if (!fnode) {
|
||||
venc_logd("drop frame.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (g_tag_fmt) {
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
copy_size = nv21_to_yuyv(yuv, width, height, fnode->mem, fnode->length);
|
||||
break;
|
||||
|
||||
case V4L2_PIX_FMT_NV21:
|
||||
copy_size = nv21_to_nv21(yuv, width, height, fnode->mem, fnode->length);
|
||||
break;
|
||||
|
||||
case V4L2_PIX_FMT_NV12:
|
||||
case V4L2_PIX_FMT_YUV420:
|
||||
case V4L2_PIX_FMT_YVU420:
|
||||
default:
|
||||
venc_logw("send default format NV21.\n");
|
||||
copy_size = nv21_to_nv21(yuv, width, height, fnode->mem, fnode->length);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (copy_size == 0) {
|
||||
venc_loge("cache size is to small.\n");
|
||||
}
|
||||
|
||||
fnode->used = copy_size;
|
||||
|
||||
venc_logd("send size(%u)\n", fnode->used);
|
||||
|
||||
put_node_to_queue(uvc_cache->ok_queue, fnode);
|
||||
}
|
||||
|
||||
//static int flag = 0;
|
||||
static void sample_yuv_save_frame(xmedia_video_frame_info vpss_frame)
|
||||
{
|
||||
char* virt_addr_y;
|
||||
xmedia_u64 phy_addr;
|
||||
xmedia_u32 size;
|
||||
xmedia_char* page_addr[2];
|
||||
xmedia_video_frame *video_frame = &vpss_frame.frame;
|
||||
xmedia_video_pixel_format pixel_fmt = video_frame->pixel_fmt;
|
||||
xmedia_video_frame_info scale_frame;
|
||||
|
||||
if (g_tag_fmt == V4L2_PIX_FMT_YUYV) {
|
||||
vgs_nv12_to_yuyv(&vpss_frame, &scale_frame);
|
||||
}
|
||||
|
||||
if (video_frame->compress_mode != XMEDIA_VIDEO_COMPRESS_MODE_NONE) {
|
||||
video_frame->stride.y_stride = video_frame->width;
|
||||
video_frame->stride.c_stride = video_frame->width;
|
||||
}
|
||||
|
||||
if ((pixel_fmt == XMEDIA_VIDEO_PIXEL_FMT_YVU_SEMIPLANAR_420) ||
|
||||
(pixel_fmt == XMEDIA_VIDEO_PIXEL_FMT_YUV_SEMIPLANAR_420)) {
|
||||
size = video_frame->stride.y_stride * video_frame->height * 3 / 2;
|
||||
} else if (pixel_fmt == XMEDIA_VIDEO_PIXEL_FMT_YUV_400) {
|
||||
size = video_frame->stride.y_stride * video_frame->height;
|
||||
} else {
|
||||
size = video_frame->stride.y_stride * video_frame->height * 3 / 2;
|
||||
}
|
||||
|
||||
if (g_tag_fmt == V4L2_PIX_FMT_YUYV) {
|
||||
phy_addr = scale_frame.frame.addr.y_phy_addr;
|
||||
size = video_frame->stride.y_stride * video_frame->height * 2;
|
||||
pixel_fmt = XMEDIA_VIDEO_PIXEL_FMT_YUYV_PACKAGE_422; //XMEDIA_VIDEO_PIXEL_FMT_YVU_SEMIPLANAR_422;
|
||||
} else {
|
||||
phy_addr = video_frame->addr.y_phy_addr;
|
||||
}
|
||||
|
||||
page_addr[0] = (xmedia_char*)xmedia_mmz_map(phy_addr, size, XMEDIA_FALSE);
|
||||
if (XMEDIA_NULL == page_addr[0]) {
|
||||
venc_loge("xmedia_mmz_map failed.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
virt_addr_y = page_addr[0];
|
||||
|
||||
debug_dump_frame((char*)virt_addr_y, size, "frame_vpss.yuv");
|
||||
sample_yuv_save_data((unsigned char*)virt_addr_y, pixel_fmt,
|
||||
video_frame->stride.y_stride, video_frame->height);
|
||||
|
||||
xmedia_mmz_unmap(page_addr[0]);
|
||||
page_addr[0] = XMEDIA_NULL;
|
||||
if (g_tag_fmt == V4L2_PIX_FMT_YUYV) {
|
||||
xmedia_vb_release_block(xmedia_vb_phy_addr_to_handle(scale_frame.frame.addr.y_head_phy_addr));
|
||||
}
|
||||
}
|
||||
|
||||
void* sample_yuv_stream_proc(void* arg)
|
||||
{
|
||||
int ret;
|
||||
xmedia_vb_base_info base_info;
|
||||
xmedia_vb_cal_cfg cal_cfg;
|
||||
xmedia_s32 millisec = -1;
|
||||
xmedia_handle vb_handle = VB_INVALID_HANDLE;
|
||||
xmedia_handle tmp_handle = VB_INVALID_HANDLE;
|
||||
xmedia_video_frame_info tmp_frame = {0};
|
||||
xmedia_video_frame_info frame_out = {0};
|
||||
|
||||
g_vpss_depth_flag = 1;
|
||||
|
||||
while (g_recv) {
|
||||
memset(&g_vpss_frame_info, 0, sizeof(xmedia_video_frame_info));
|
||||
g_vpss_frame_info.pool_id = VB_INVALID_POOLID;
|
||||
|
||||
ret = xmedia_vpss_acquire_ochn_frame(g_vpss_pipe, g_vpss_ochn, &g_vpss_frame_info, millisec);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_logi("piipe(%d) ochn(%d) get frame failed!\n", g_vpss_pipe, g_vpss_ochn);
|
||||
usleep(1000);
|
||||
continue;
|
||||
}
|
||||
|
||||
venc_logd("pipe(%d) ochn(%d) video_fmt(%d) save frame!\n", g_vpss_pipe, g_vpss_ochn,
|
||||
g_vpss_frame_info.frame.video_fmt);
|
||||
|
||||
if (g_vpss_frame_info.frame.video_fmt != XMEDIA_VIDEO_FMT_LINEAR) {
|
||||
memset(&base_info, 0, sizeof(xmedia_vb_base_info));
|
||||
base_info.align = 0;
|
||||
base_info.bit_width = g_vpss_frame_info.frame.bit_width;
|
||||
base_info.cmp_mode = XMEDIA_VIDEO_COMPRESS_MODE_NONE;
|
||||
base_info.width = g_vpss_frame_info.frame.width;
|
||||
base_info.height = g_vpss_frame_info.frame.height;
|
||||
base_info.pixel_fmt = g_vpss_frame_info.frame.pixel_fmt;
|
||||
|
||||
if (g_vpss_frame_info.frame.video_fmt == XMEDIA_VIDEO_FMT_TILE_16x4) {
|
||||
base_info.video_fmt = XMEDIA_VIDEO_FMT_TILE_64x4;
|
||||
ret = xmedia_vb_get_buffer_config(&base_info, &cal_cfg);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_logw("get buffer config failed with 0x%x\n", ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
tmp_handle = xmedia_vb_get_block(-1, cal_cfg.vb_size, NULL);
|
||||
if (tmp_handle == VB_INVALID_HANDLE) {
|
||||
venc_logw("get block failed\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(&tmp_frame, &g_vpss_frame_info, sizeof(xmedia_video_frame_info));
|
||||
tmp_frame.pool_id = xmedia_vb_handle_to_pool_id(tmp_handle);
|
||||
tmp_frame.frame.addr.y_phy_addr = xmedia_vb_handle_to_phy_addr(tmp_handle);
|
||||
tmp_frame.frame.addr.y_head_phy_addr = tmp_frame.frame.addr.y_phy_addr;
|
||||
tmp_frame.frame.addr.c_phy_addr = tmp_frame.frame.addr.y_phy_addr + cal_cfg.main_y_size;
|
||||
tmp_frame.frame.stride.y_stride = cal_cfg.main_stride;
|
||||
tmp_frame.frame.stride.c_stride = cal_cfg.main_stride;
|
||||
tmp_frame.frame.compress_mode = XMEDIA_VIDEO_COMPRESS_MODE_NONE;
|
||||
tmp_frame.frame.video_fmt = XMEDIA_VIDEO_FMT_TILE_64x4;
|
||||
} else {
|
||||
memcpy(&tmp_frame, &g_vpss_frame_info, sizeof(xmedia_video_frame_info));
|
||||
}
|
||||
|
||||
base_info.video_fmt = XMEDIA_VIDEO_FMT_LINEAR;
|
||||
ret = xmedia_vb_get_buffer_config(&base_info, &cal_cfg);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
if (tmp_handle != VB_INVALID_HANDLE) {
|
||||
xmedia_vb_release_block(tmp_handle);
|
||||
tmp_handle = VB_INVALID_HANDLE;
|
||||
}
|
||||
venc_logw("get buffer config failed with 0x%x\n", ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
vb_handle = xmedia_vb_get_block(-1, cal_cfg.vb_size, NULL);
|
||||
if (vb_handle == VB_INVALID_HANDLE) {
|
||||
if (tmp_handle != VB_INVALID_HANDLE) {
|
||||
xmedia_vb_release_block(tmp_handle);
|
||||
tmp_handle = VB_INVALID_HANDLE;
|
||||
}
|
||||
venc_logw("get block failed\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(&frame_out, &tmp_frame, sizeof(xmedia_video_frame_info));
|
||||
frame_out.pool_id = xmedia_vb_handle_to_pool_id(vb_handle);
|
||||
frame_out.frame.addr.y_phy_addr = xmedia_vb_handle_to_phy_addr(vb_handle);
|
||||
frame_out.frame.addr.y_head_phy_addr = frame_out.frame.addr.y_phy_addr;
|
||||
frame_out.frame.addr.c_phy_addr = frame_out.frame.addr.y_phy_addr + cal_cfg.main_y_size;
|
||||
frame_out.frame.stride.y_stride = cal_cfg.main_stride;
|
||||
frame_out.frame.stride.c_stride = cal_cfg.main_stride;
|
||||
frame_out.frame.compress_mode = XMEDIA_VIDEO_COMPRESS_MODE_NONE;
|
||||
frame_out.frame.video_fmt = XMEDIA_VIDEO_FMT_LINEAR;
|
||||
|
||||
ret = vpss_ochn_dump_transform_format(&g_vpss_frame_info, &tmp_frame, &frame_out);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
if (tmp_handle != VB_INVALID_HANDLE) {
|
||||
xmedia_vb_release_block(tmp_handle);
|
||||
tmp_handle = VB_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (vb_handle != VB_INVALID_HANDLE) {
|
||||
xmedia_vb_release_block(vb_handle);
|
||||
vb_handle = VB_INVALID_HANDLE;
|
||||
}
|
||||
venc_logw("pipe %d ochn %d vpss_ochn_dump_transform_format failed\n", g_vpss_pipe, g_vpss_ochn);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
memcpy(&frame_out, &g_vpss_frame_info, sizeof(xmedia_video_frame_info));
|
||||
}
|
||||
|
||||
sample_yuv_save_frame(frame_out);
|
||||
|
||||
if (tmp_handle != VB_INVALID_HANDLE) {
|
||||
xmedia_vb_release_block(tmp_handle);
|
||||
tmp_handle = VB_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (vb_handle != VB_INVALID_HANDLE) {
|
||||
xmedia_vb_release_block(vb_handle);
|
||||
vb_handle = VB_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
ret = xmedia_vpss_release_ochn_frame(g_vpss_pipe, g_vpss_ochn, &g_vpss_frame_info);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
printf("pipe(%d) ochn(%d) release frame failed!\n", g_vpss_pipe, g_vpss_ochn);
|
||||
}
|
||||
}
|
||||
|
||||
g_vpss_frame_info.pool_id = VB_INVALID_POOLID;
|
||||
|
||||
if (tmp_handle != VB_INVALID_HANDLE) {
|
||||
xmedia_vb_release_block(tmp_handle);
|
||||
tmp_handle = VB_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (vb_handle != VB_INVALID_HANDLE) {
|
||||
xmedia_vb_release_block(vb_handle);
|
||||
vb_handle = VB_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int sample_yuv_start_get_stream(int venc_chn, int cnt)
|
||||
{
|
||||
int rc;
|
||||
|
||||
g_recv = 1;
|
||||
|
||||
rc = pthread_create(&g_recv_pid, NULL, sample_yuv_stream_proc, NULL);
|
||||
if (rc < 0) {
|
||||
venc_loge("Create sample_yuv_stream_proc failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sample_yuv_stop_get_stream(void)
|
||||
{
|
||||
|
||||
if (g_recv == 1) {
|
||||
g_recv = 0;
|
||||
pthread_join(g_recv_pid, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sample_yuv_normalp_classic(void)
|
||||
{
|
||||
xmedia_s32 ret = XMEDIA_SUCCESS;
|
||||
|
||||
xmedia_u32 framerate = 0, fps_user = 0;
|
||||
xmedia_video_size pic_size = {0};
|
||||
xmedia_video_size vpss_ochn_size = {0};
|
||||
xmedia_isp_config isp_config = {0};
|
||||
xmedia_payload_type en_payload = PT_MJPEG;
|
||||
vi_sensor_info sensor_info = {0};
|
||||
|
||||
g_vi_config.dev_info[g_vi_dev].dev_en = XMEDIA_TRUE;
|
||||
g_vi_config.dev_info[g_vi_dev].dev_no = g_vi_dev;
|
||||
g_vi_config.dev_info[g_vi_dev].sensor_type = g_sensor_type;
|
||||
g_vi_config.pipe_info[g_vi_pipe].pipe_en = XMEDIA_TRUE;
|
||||
g_vi_config.pipe_info[g_vi_pipe].pipe_no = g_vi_pipe;
|
||||
g_vi_config.pipe_info[g_vi_pipe].chn_info[g_vi_chn].chn_en = XMEDIA_TRUE;
|
||||
g_vi_config.pipe_info[g_vi_pipe].chn_info[g_vi_chn].chn_no = g_vi_chn;
|
||||
g_vi_config.dev_bind_pipe[g_vi_dev].pipe[0] = g_vi_pipe;
|
||||
g_vi_config.dev_bind_pipe[g_vi_dev].pipe[1] = -1;
|
||||
|
||||
sample_comm_vi_get_sensor_info(g_sensor_type, &sensor_info);
|
||||
sample_comm_vi_get_framerate_by_sensor(g_sensor_type, &framerate);
|
||||
|
||||
get_user_format(NULL, NULL, &fps_user);
|
||||
if (fps_user > framerate) {
|
||||
venc_logw("User framerate(%d) > sensor framerate(%d), used sensor framerate\n", fps_user, framerate);
|
||||
} else {
|
||||
framerate = fps_user;
|
||||
}
|
||||
|
||||
venc_logi("isp framerate(%d)\n", framerate);
|
||||
isp_config.fps = framerate;
|
||||
isp_config.mode_config.work_mode = XMEDIA_ISP_WORK_MODE_MASTER;
|
||||
isp_config.mode_config.master_mode.blend_stat_enable = XMEDIA_FALSE;
|
||||
isp_config.mode_config.master_mode.slave_num = 0;
|
||||
isp_config.pixel_fmt = sensor_info.pixel_format;
|
||||
isp_config.size.height = sensor_info.height;
|
||||
isp_config.size.width = sensor_info.width;
|
||||
pic_size.height = sensor_info.height;
|
||||
pic_size.width = sensor_info.width;
|
||||
isp_config.wdr_mode = sensor_info.wdr_mode;
|
||||
|
||||
// isp pipe init
|
||||
g_isp_param.pipe[g_vi_pipe] = g_vi_pipe;
|
||||
g_isp_param.isp_info[g_vi_pipe].sensor_type = g_sensor_type;
|
||||
g_isp_param.isp_info[g_vi_pipe].flip = XMEDIA_FALSE;
|
||||
g_isp_param.isp_info[g_vi_pipe].mirror = XMEDIA_FALSE;
|
||||
g_isp_param.isp_info[g_vi_pipe].isp_pipe_en = XMEDIA_TRUE;
|
||||
g_isp_param.isp_info[g_vi_pipe].isp_sensor_en = XMEDIA_TRUE;
|
||||
|
||||
memcpy(&(g_isp_param.isp_info[g_vi_pipe].isp_config), &isp_config, sizeof(xmedia_isp_config));
|
||||
|
||||
// isp init
|
||||
ret = sample_comm_isp_init(&g_isp_param, &g_vi_config);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("isp init failed!\n");
|
||||
goto exit0;
|
||||
}
|
||||
|
||||
// vi start
|
||||
ret = sample_comm_vi_start(&g_vi_config, &g_video_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("start vi failed!\n");
|
||||
goto exit1;
|
||||
}
|
||||
|
||||
// isp start
|
||||
ret = sample_comm_isp_start(&g_isp_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("start isp failed!\n");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
// vpss config
|
||||
ret = sample_comm_vpss_get_default_pipe_cfg(&g_vpss_config.pipe_info[g_vpss_pipe].pipe_config, pic_size, &g_video_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("get default pipe cfg failed !\n");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
get_user_format(&en_payload, &pic_size, NULL);
|
||||
#if 0
|
||||
vpss_ochn_size.width = sensor_info.width;
|
||||
vpss_ochn_size.height = sensor_info.height;
|
||||
#else
|
||||
vpss_ochn_size.width = pic_size.width;
|
||||
vpss_ochn_size.height = pic_size.height;
|
||||
#endif
|
||||
|
||||
g_vpss_config.pipe_info[g_vpss_pipe].pipe_en = XMEDIA_TRUE;
|
||||
g_vpss_config.pipe_info[g_vpss_pipe].pipe_no = g_vpss_pipe;
|
||||
|
||||
g_vpss_config.pipe_info[g_vpss_pipe].chn_info[g_vpss_ochn].chn_en = XMEDIA_TRUE;
|
||||
g_vpss_config.pipe_info[g_vpss_pipe].chn_info[g_vpss_ochn].chn_no = g_vpss_ochn;
|
||||
ret = sample_comm_vpss_get_default_ochn_cfg(&g_vpss_config.pipe_info[g_vpss_pipe].chn_info[g_vpss_ochn].chn_config,
|
||||
vpss_ochn_size, &g_video_param);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("get default pipe cfg failed !\n");
|
||||
goto exit2;
|
||||
}
|
||||
#if 0
|
||||
ret = sample_comm_vpss_set_ochn_wrap(&g_vpss_config.pipe_info[g_vpss_pipe].chn_info[g_vpss_ochn]);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("get default pipe cfg failed !\n");
|
||||
goto exit2;
|
||||
}
|
||||
#endif
|
||||
|
||||
// vpss start
|
||||
ret = sample_comm_vpss_start(&g_vpss_config);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("vpss start failed !\n");
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
ret = sample_comm_sys_vi_bind_vpss(g_vi_pipe, g_vi_chn, g_vpss_pipe, g_vpss_ichn);
|
||||
if (ret != XMEDIA_SUCCESS) {
|
||||
venc_loge("vi bind vpss failed !\n");
|
||||
goto exit3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit3:
|
||||
sample_comm_vpss_stop(&g_vpss_config);
|
||||
exit2:
|
||||
sample_comm_isp_stop(&g_isp_param);
|
||||
exit1:
|
||||
sample_comm_vi_stop(&g_vi_config);
|
||||
exit0:
|
||||
sample_comm_isp_exit(&g_isp_param);
|
||||
|
||||
sample_yuv_sys_deinit();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* Processing Unit Operation Functions
|
||||
*******************************************************/
|
||||
|
||||
/* Processing Unit Operation Functions End */
|
||||
|
||||
/*******************************************************
|
||||
* Input Terminal Operation Functions
|
||||
*******************************************************/
|
||||
|
||||
/* Input Terminal Operation Functions End */
|
||||
|
||||
/*******************************************************
|
||||
* Stream Control Operation Functions
|
||||
*******************************************************/
|
||||
int sample_yuv_set_idr(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_yuv_init(void)
|
||||
{
|
||||
g_sensor_type = SENSOR0_TYPE;
|
||||
sample_yuv_sys_init(g_sensor_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_yuv_startup(int fmt)
|
||||
{
|
||||
g_start = 1;
|
||||
|
||||
if (sample_yuv_normalp_classic() < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_tag_fmt = fmt;
|
||||
if (g_tag_fmt == V4L2_PIX_FMT_YUYV) {
|
||||
sample_yuv_create_yuyv_pool();
|
||||
}
|
||||
sample_yuv_start_get_stream(g_vpss_ochn, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample_yuv_shutdown(void)
|
||||
{
|
||||
if (!g_start) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sample_yuv_stop_get_stream();
|
||||
|
||||
sample_comm_sys_vi_unbind_vpss(g_vi_pipe, g_vi_chn, g_vpss_pipe, g_vpss_ichn);
|
||||
|
||||
sample_comm_vpss_stop(&g_vpss_config);
|
||||
|
||||
sample_comm_isp_stop(&g_isp_param);
|
||||
|
||||
sample_comm_vi_stop(&g_vi_config);
|
||||
|
||||
sample_comm_isp_exit(&g_isp_param);
|
||||
|
||||
sample_yuv_sys_deinit();
|
||||
|
||||
g_start = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __SAMPLE_VIDEO_YUV_H__
|
||||
#define __SAMPLE_VIDEO_YUV_H__
|
||||
|
||||
int sample_yuv_set_idr(void);
|
||||
int sample_yuv_init(void);
|
||||
int sample_yuv_startup(int fmt);
|
||||
int sample_yuv_shutdown(void);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
#ifndef __STREAM_H__
|
||||
#define __STREAM_H__
|
||||
|
||||
#include "uvc_hal.h"
|
||||
#include "sample_video.h"
|
||||
|
||||
//USB_Video_Payload_H_264_1.0 3.3
|
||||
#define UVCX_PICTURE_TYPE_CONTROL 0x09
|
||||
|
||||
// H264 extension unit id, must same as to driver
|
||||
#define UNIT_XU_H264 10
|
||||
|
||||
// CAMERA
|
||||
#define UNIT_XU_CAMERA (0x11)
|
||||
|
||||
// VENDOR
|
||||
#define UNIT_XU_VENDOR 11
|
||||
|
||||
typedef struct stream_control_ops {
|
||||
int (*init)(void);
|
||||
int (*deinit)(void);
|
||||
int (*startup)(void);
|
||||
int (*shutdown)(void);
|
||||
int (*set_idr)(void);
|
||||
int (*set_property)(struct encoder_property *p);
|
||||
} stream_control_ops_st;
|
||||
|
||||
typedef struct processing_unit_ops {
|
||||
uint16_t (*brightness_get)(void);
|
||||
uint16_t (*contrast_get)(void);
|
||||
uint16_t (*hue_get)(void);
|
||||
uint8_t (*power_line_frequency_get)(void);
|
||||
uint16_t (*saturation_get)(void);
|
||||
uint8_t (*white_balance_temperature_auto_get)(void);
|
||||
uint16_t (*white_balance_temperature_get)(void);
|
||||
|
||||
void (*brightness_set)(uint16_t v);
|
||||
void (*contrast_set)(uint16_t v);
|
||||
void (*hue_set)(uint16_t v);
|
||||
void (*power_line_frequency_set)(uint8_t v);
|
||||
void (*saturation_set)(uint16_t v);
|
||||
void (*white_balance_temperature_auto_set)(uint8_t v);
|
||||
void (*white_balance_temperature_set)(uint16_t v);
|
||||
} processing_unit_ops_st;
|
||||
|
||||
typedef struct input_terminal_ops {
|
||||
uint32_t (*exposure_ansolute_time_get)(void);
|
||||
uint8_t (*exposure_auto_mode_get)(void);
|
||||
void (*exposure_ansolute_time_set)(uint32_t v);
|
||||
void (*exposure_auto_mode_set)(uint8_t v);
|
||||
} input_terminal_ops_st;
|
||||
|
||||
typedef struct extension_unit_ops {
|
||||
/*todo eu-h264 ops*/
|
||||
|
||||
/*todo eu-camera ops*/
|
||||
} extension_unit_ops_st;
|
||||
|
||||
typedef struct stream
|
||||
{
|
||||
struct stream_control_ops *mpi_sc_ops;
|
||||
struct processing_unit_ops *mpi_pu_ops;
|
||||
struct input_terminal_ops *mpi_it_ops;
|
||||
struct extension_unit_ops *mpi_eu_ops;
|
||||
int streaming;
|
||||
int exposure_auto_stall;
|
||||
int brightness_stall;
|
||||
} stream;
|
||||
|
||||
/* media control functions */
|
||||
extern int stream_register_mpi_ops(struct stream_control_ops *sc_ops,
|
||||
struct processing_unit_ops *pu_ops,
|
||||
struct input_terminal_ops *it_ops,
|
||||
struct extension_unit_ops *eu_ops);
|
||||
|
||||
extern int video_stream_set_enc_property(struct encoder_property *p);
|
||||
extern int video_stream_init(void);
|
||||
extern int video_stream_deinit(void);
|
||||
extern int video_stream_shutdown(void);
|
||||
extern int video_stream_startup(void);
|
||||
extern int video_stream_set_enc_idr(void);
|
||||
|
||||
extern unsigned int stream_pu_get_brightness(void);
|
||||
extern void stream_pu_set_brightness(unsigned int val);
|
||||
extern unsigned int stream_pu_get_contrast(void);
|
||||
extern void stream_pu_set_contrast(unsigned int val);
|
||||
|
||||
extern void stream_event_pu_setup(struct uvc_device *dev,
|
||||
uint8_t req,
|
||||
uint8_t unit_id,
|
||||
uint8_t cs,
|
||||
struct uvc_request_data* resp);
|
||||
|
||||
extern void stream_event_it_setup(struct uvc_device *dev,
|
||||
uint8_t req,
|
||||
uint8_t unit_id,
|
||||
uint8_t cs,
|
||||
struct uvc_request_data *resp);
|
||||
|
||||
extern void stream_event_eu_h264_setup(uint8_t req,
|
||||
uint8_t unit_id,
|
||||
uint8_t cs,
|
||||
struct uvc_request_data *resp);
|
||||
|
||||
extern void stream_event_eu_camera_setup(uint8_t req,
|
||||
uint8_t unit_id,
|
||||
uint8_t cs,
|
||||
struct uvc_request_data *resp);
|
||||
|
||||
extern void stream_event_eu_vendor1_setup(struct uvc_device *dev,
|
||||
uint8_t req,
|
||||
uint8_t unit_id,
|
||||
uint8_t cs,
|
||||
struct uvc_request_data *resp);
|
||||
|
||||
extern void stream_event_eu_h264_data(int unit_id, int control, struct uvc_request_data *data);
|
||||
extern void stream_event_pu_data(int unit_id, int control, struct uvc_request_data *data);
|
||||
extern void stream_event_it_data(int unit_id, int control, struct uvc_request_data *data);
|
||||
extern void stream_event_eu_camera_data(int unit_id, int control, struct uvc_request_data *data);
|
||||
extern void stream_event_eu_vendor1_data(int unit_id, int control, struct uvc_request_data *data);
|
||||
|
||||
|
||||
#endif //__STREAM_H__
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "uvc.h"
|
||||
#include "uvc_hal.h"
|
||||
#include "video_stream.h"
|
||||
|
||||
#include "uvc_log.h"
|
||||
|
||||
extern void video_stream_register(void);
|
||||
|
||||
static int g_uvc_run = 0;
|
||||
|
||||
static int __uvc_init(void)
|
||||
{
|
||||
video_stream_register();
|
||||
video_stream_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __uvc_deinit(void)
|
||||
{
|
||||
|
||||
video_stream_deinit();
|
||||
video_stream_unregister();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __uvc_open(void)
|
||||
{
|
||||
return open_uvc_device();
|
||||
}
|
||||
|
||||
static int __uvc_close(void)
|
||||
{
|
||||
return close_uvc_device();
|
||||
}
|
||||
|
||||
static pthread_t g_pid_stream;
|
||||
static pthread_t g_pid_contrl;
|
||||
|
||||
static void *uvc_stream_process(void *args)
|
||||
{
|
||||
uvc_logt("UVC stream process start.\n");
|
||||
while (g_uvc_run) {
|
||||
run_uvc_data();
|
||||
}
|
||||
|
||||
uvc_logt("UVC stream process exit.\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *uvc_contrl_process(void *args)
|
||||
{
|
||||
uvc_logt("UVC contrl process start.\n");
|
||||
while (g_uvc_run) {
|
||||
run_uvc_device();
|
||||
}
|
||||
|
||||
uvc_logt("UVC contrl process exit.\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
static int __uvc_run(void)
|
||||
{
|
||||
int err;
|
||||
g_uvc_run = 1;
|
||||
|
||||
uvc_logt("UVC run entry.\n");
|
||||
err = pthread_create(&g_pid_stream, NULL, &uvc_stream_process, NULL);
|
||||
if (err != 0) {
|
||||
uvc_loge("Create uvc_stream_process failed.\n");
|
||||
}
|
||||
|
||||
err = pthread_create(&g_pid_contrl, NULL, &uvc_contrl_process, NULL);
|
||||
if (err != 0) {
|
||||
uvc_loge("Create uvc_contrl_process failed.\n");
|
||||
}
|
||||
|
||||
uvc_logt("UVC run ok.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __uvc_stop(void)
|
||||
{
|
||||
uvc_logt("UVC stop entry.\n");
|
||||
g_uvc_run = 0;
|
||||
pthread_join(g_pid_contrl, NULL);
|
||||
pthread_join(g_pid_stream, NULL);
|
||||
uvc_logt("UVC stop ok.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static uvc_t __uvc = {
|
||||
.init = &__uvc_init,
|
||||
.deinit = &__uvc_deinit,
|
||||
.open = &__uvc_open,
|
||||
.close = &__uvc_close,
|
||||
.run = &__uvc_run,
|
||||
.stop = &__uvc_stop,
|
||||
};
|
||||
|
||||
uvc_t *get_uvc()
|
||||
{
|
||||
return &__uvc;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) XMEDIA. All rights reserved.
|
||||
*/
|
||||
#ifndef __UVC_LOG_H__
|
||||
#define __UVC_LOG_H__
|
||||
|
||||
#include "log_base.h"
|
||||
|
||||
#undef TAG
|
||||
#define TAG "UVC"
|
||||
|
||||
#define uvc_loge LOGE
|
||||
#define uvc_logw LOGW
|
||||
#define uvc_logi LOGI
|
||||
#define uvc_logt LOGT
|
||||
#define uvc_logd LOGD
|
||||
|
||||
#define func_entry() uvc_logd("entry\n")
|
||||
#define func_success() uvc_logd("success\n")
|
||||
#define func_fail() uvc_loge("failed\n")
|
||||
|
||||
#define check_null_goto(ptr, tag) do { \
|
||||
if ((ptr) == NULL) { \
|
||||
uvc_loge("invalid param.\n"); \
|
||||
goto tag; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user