GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
+381
View File
@@ -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
+11
View File
@@ -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
+96
View File
@@ -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
+27
View File
@@ -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).
+26
View File
@@ -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
+172
View File
@@ -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;
}
+52
View File
@@ -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 $@
+815
View File
@@ -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
/**************************************************************************************/
+71
View File
@@ -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
+127
View File
@@ -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
+569
View File
@@ -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
+117
View File
@@ -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;
}
+18
View File
@@ -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
+138
View File
@@ -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;
}
+20
View File
@@ -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__
+133
View File
@@ -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;
}
+13
View File
@@ -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__
+513
View File
@@ -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"
+182
View File
@@ -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
+71
View File
@@ -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();
}
+18
View File
@@ -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
+504
View File
@@ -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);
}
}
+58
View File
@@ -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__
+952
View File
@@ -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 */
+340
View File
@@ -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
+59
View File
@@ -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;
}
+14
View File
@@ -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__
+73
View File
@@ -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
+28
View File
@@ -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
+25
View File
@@ -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
+25
View File
@@ -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
+24
View File
@@ -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 */
+65
View File
@@ -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
+502
View File
@@ -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");
}
}
+75
View File
@@ -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
+57
View File
@@ -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__
+292
View File
@@ -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));
}
+265
View File
@@ -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
+321
View File
@@ -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
+127
View File
@@ -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__
+114
View File
@@ -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;
}
+29
View File
@@ -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