1, 整理二次开发代码, 大部分变更部分增加了以 //chenxiaolei 开头的注释

This commit is contained in:
chenlei 2019-07-26 09:58:56 +08:00
parent 7aa0c2ae33
commit 58a49d5f97
47 changed files with 227841 additions and 103 deletions

9
.dockerignore Normal file
View File

@ -0,0 +1,9 @@
.git
.idea
cmake-build-debug
build
mac_build
docker/Dockerfile
linux_build
android_build
ios_build

12
.gitignore vendored
View File

@ -27,10 +27,16 @@
*.out
*.app
/X64/
.idea
*.DS_Store
/cmake-build-debug/
/.idea/
cmake-build-debug
build
mac_build
linux_build
android_build
ios_build
/c_wrapper/.idea/

View File

@ -0,0 +1,73 @@
#
# Copyright (c) 2008-2018 the Urho3D project.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# Define target name
set (TARGET_NAME sqlite)
# Define preprocessor macros
add_definitions (-DSQLITE_USE_URI=1 -DSQLITE_ENABLE_COLUMN_METADATA -DSQLITE_SOUNDEX) # https://www.sqlite.org/compile.html
if (WEB)
# Do not use pthread and dl libraries for Web platform
add_definitions (-DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION)
elseif (MINGW)
# We only support MinGW with "_mingw.h" header file, note the leading underscore
add_definitions (-D_HAVE__MINGW_H)
endif ()
foreach (VAR HAVE_STDINT_H HAVE_INTTYPES_H HAVE_MALLOC_H HAVE_MALLOC_USABLE_SIZE)
if (${VAR})
add_definitions (-D${VAR})
endif ()
endforeach ()
# Define source files
set (SOURCE_FILES src/sqlite3.c)
# Setup target
setup_library ()
# Install headers for building and using the Urho3D library
install_header_files (DIRECTORY src/ DESTINATION ${DEST_INCLUDE_DIR}/ThirdParty/SQLite FILES_MATCHING PATTERN *.h) # Note: the trailing slash is significant
# Setup additional SQLite CLI standalone target (this target can be transfered and executed on an embedded device, such as Raspberry Pi and Android)
if (NOT IOS AND NOT TVOS AND NOT WEB)
# Define target name for SQLite shell
set (TARGET_NAME isql)
# Define source files
set (SOURCE_FILES src/shell.c src/sqlite3.c src/sqlite3.h)
# Define dependency libs
if (NOT WIN32)
set (LIBS dl)
if (READLINE_FOUND)
add_definitions (-DHAVE_READLINE)
list (APPEND INCLUDE_DIRS ${READLINE_INCLUDE_DIRS})
list (APPEND LIBS ${READLINE_LIBRARIES})
endif ()
endif ()
# Setup target
setup_executable (NODEPS)
endif ()
# SQLite will not work correctly with the -ffast-math option, so unset it in this scope only
string (REPLACE -ffast-math "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") # Stringify for string replacement

8398
3rdpart/SQLite/src/shell.c Normal file

File diff suppressed because it is too large Load Diff

204434
3rdpart/SQLite/src/sqlite3.c Normal file

File diff suppressed because it is too large Load Diff

10694
3rdpart/SQLite/src/sqlite3.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,578 @@
/*
** 2006 June 7
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the SQLite interface for use by
** shared libraries that want to be imported as extensions into
** an SQLite instance. Shared libraries that intend to be loaded
** as extensions by SQLite should #include this file instead of
** sqlite3.h.
*/
#ifndef SQLITE3EXT_H
#define SQLITE3EXT_H
#include "sqlite3.h"
/*
** The following structure holds pointers to all of the SQLite API
** routines.
**
** WARNING: In order to maintain backwards compatibility, add new
** interfaces to the end of this structure only. If you insert new
** interfaces in the middle of this structure, then older different
** versions of SQLite will not be able to load each other's shared
** libraries!
*/
struct sqlite3_api_routines {
void * (*aggregate_context)(sqlite3_context*,int nBytes);
int (*aggregate_count)(sqlite3_context*);
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
int (*bind_double)(sqlite3_stmt*,int,double);
int (*bind_int)(sqlite3_stmt*,int,int);
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
int (*bind_null)(sqlite3_stmt*,int);
int (*bind_parameter_count)(sqlite3_stmt*);
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
int (*busy_timeout)(sqlite3*,int ms);
int (*changes)(sqlite3*);
int (*close)(sqlite3*);
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const char*));
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const void*));
const void * (*column_blob)(sqlite3_stmt*,int iCol);
int (*column_bytes)(sqlite3_stmt*,int iCol);
int (*column_bytes16)(sqlite3_stmt*,int iCol);
int (*column_count)(sqlite3_stmt*pStmt);
const char * (*column_database_name)(sqlite3_stmt*,int);
const void * (*column_database_name16)(sqlite3_stmt*,int);
const char * (*column_decltype)(sqlite3_stmt*,int i);
const void * (*column_decltype16)(sqlite3_stmt*,int);
double (*column_double)(sqlite3_stmt*,int iCol);
int (*column_int)(sqlite3_stmt*,int iCol);
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
const char * (*column_name)(sqlite3_stmt*,int);
const void * (*column_name16)(sqlite3_stmt*,int);
const char * (*column_origin_name)(sqlite3_stmt*,int);
const void * (*column_origin_name16)(sqlite3_stmt*,int);
const char * (*column_table_name)(sqlite3_stmt*,int);
const void * (*column_table_name16)(sqlite3_stmt*,int);
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
const void * (*column_text16)(sqlite3_stmt*,int iCol);
int (*column_type)(sqlite3_stmt*,int iCol);
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
int (*complete)(const char*sql);
int (*complete16)(const void*sql);
int (*create_collation)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_collation16)(sqlite3*,const void*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_function)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_function16)(sqlite3*,const void*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
int (*data_count)(sqlite3_stmt*pStmt);
sqlite3 * (*db_handle)(sqlite3_stmt*);
int (*declare_vtab)(sqlite3*,const char*);
int (*enable_shared_cache)(int);
int (*errcode)(sqlite3*db);
const char * (*errmsg)(sqlite3*);
const void * (*errmsg16)(sqlite3*);
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
int (*expired)(sqlite3_stmt*);
int (*finalize)(sqlite3_stmt*pStmt);
void (*free)(void*);
void (*free_table)(char**result);
int (*get_autocommit)(sqlite3*);
void * (*get_auxdata)(sqlite3_context*,int);
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
int (*global_recover)(void);
void (*interruptx)(sqlite3*);
sqlite_int64 (*last_insert_rowid)(sqlite3*);
const char * (*libversion)(void);
int (*libversion_number)(void);
void *(*malloc)(int);
char * (*mprintf)(const char*,...);
int (*open)(const char*,sqlite3**);
int (*open16)(const void*,sqlite3**);
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
void *(*realloc)(void*,int);
int (*reset)(sqlite3_stmt*pStmt);
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_double)(sqlite3_context*,double);
void (*result_error)(sqlite3_context*,const char*,int);
void (*result_error16)(sqlite3_context*,const void*,int);
void (*result_int)(sqlite3_context*,int);
void (*result_int64)(sqlite3_context*,sqlite_int64);
void (*result_null)(sqlite3_context*);
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_value)(sqlite3_context*,sqlite3_value*);
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
const char*,const char*),void*);
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
char * (*snprintf)(int,char*,const char*,...);
int (*step)(sqlite3_stmt*);
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
char const**,char const**,int*,int*,int*);
void (*thread_cleanup)(void);
int (*total_changes)(sqlite3*);
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
sqlite_int64),void*);
void * (*user_data)(sqlite3_context*);
const void * (*value_blob)(sqlite3_value*);
int (*value_bytes)(sqlite3_value*);
int (*value_bytes16)(sqlite3_value*);
double (*value_double)(sqlite3_value*);
int (*value_int)(sqlite3_value*);
sqlite_int64 (*value_int64)(sqlite3_value*);
int (*value_numeric_type)(sqlite3_value*);
const unsigned char * (*value_text)(sqlite3_value*);
const void * (*value_text16)(sqlite3_value*);
const void * (*value_text16be)(sqlite3_value*);
const void * (*value_text16le)(sqlite3_value*);
int (*value_type)(sqlite3_value*);
char *(*vmprintf)(const char*,va_list);
/* Added ??? */
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
/* Added by 3.3.13 */
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
int (*clear_bindings)(sqlite3_stmt*);
/* Added by 3.4.1 */
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
void (*xDestroy)(void *));
/* Added by 3.5.0 */
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
int (*blob_bytes)(sqlite3_blob*);
int (*blob_close)(sqlite3_blob*);
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
int,sqlite3_blob**);
int (*blob_read)(sqlite3_blob*,void*,int,int);
int (*blob_write)(sqlite3_blob*,const void*,int,int);
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*),
void(*)(void*));
int (*file_control)(sqlite3*,const char*,int,void*);
sqlite3_int64 (*memory_highwater)(int);
sqlite3_int64 (*memory_used)(void);
sqlite3_mutex *(*mutex_alloc)(int);
void (*mutex_enter)(sqlite3_mutex*);
void (*mutex_free)(sqlite3_mutex*);
void (*mutex_leave)(sqlite3_mutex*);
int (*mutex_try)(sqlite3_mutex*);
int (*open_v2)(const char*,sqlite3**,int,const char*);
int (*release_memory)(int);
void (*result_error_nomem)(sqlite3_context*);
void (*result_error_toobig)(sqlite3_context*);
int (*sleep)(int);
void (*soft_heap_limit)(int);
sqlite3_vfs *(*vfs_find)(const char*);
int (*vfs_register)(sqlite3_vfs*,int);
int (*vfs_unregister)(sqlite3_vfs*);
int (*xthreadsafe)(void);
void (*result_zeroblob)(sqlite3_context*,int);
void (*result_error_code)(sqlite3_context*,int);
int (*test_control)(int, ...);
void (*randomness)(int,void*);
sqlite3 *(*context_db_handle)(sqlite3_context*);
int (*extended_result_codes)(sqlite3*,int);
int (*limit)(sqlite3*,int,int);
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
const char *(*sql)(sqlite3_stmt*);
int (*status)(int,int*,int*,int);
int (*backup_finish)(sqlite3_backup*);
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
int (*backup_pagecount)(sqlite3_backup*);
int (*backup_remaining)(sqlite3_backup*);
int (*backup_step)(sqlite3_backup*,int);
const char *(*compileoption_get)(int);
int (*compileoption_used)(const char*);
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void(*xDestroy)(void*));
int (*db_config)(sqlite3*,int,...);
sqlite3_mutex *(*db_mutex)(sqlite3*);
int (*db_status)(sqlite3*,int,int*,int*,int);
int (*extended_errcode)(sqlite3*);
void (*log)(int,const char*,...);
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
const char *(*sourceid)(void);
int (*stmt_status)(sqlite3_stmt*,int,int);
int (*strnicmp)(const char*,const char*,int);
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
int (*wal_autocheckpoint)(sqlite3*,int);
int (*wal_checkpoint)(sqlite3*,const char*);
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
int (*vtab_config)(sqlite3*,int op,...);
int (*vtab_on_conflict)(sqlite3*);
/* Version 3.7.16 and later */
int (*close_v2)(sqlite3*);
const char *(*db_filename)(sqlite3*,const char*);
int (*db_readonly)(sqlite3*,const char*);
int (*db_release_memory)(sqlite3*);
const char *(*errstr)(int);
int (*stmt_busy)(sqlite3_stmt*);
int (*stmt_readonly)(sqlite3_stmt*);
int (*stricmp)(const char*,const char*);
int (*uri_boolean)(const char*,const char*,int);
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
const char *(*uri_parameter)(const char*,const char*);
char *(*vsnprintf)(int,char*,const char*,va_list);
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
/* Version 3.8.7 and later */
int (*auto_extension)(void(*)(void));
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
void(*)(void*));
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
void(*)(void*),unsigned char);
int (*cancel_auto_extension)(void(*)(void));
int (*load_extension)(sqlite3*,const char*,const char*,char**);
void *(*malloc64)(sqlite3_uint64);
sqlite3_uint64 (*msize)(void*);
void *(*realloc64)(void*,sqlite3_uint64);
void (*reset_auto_extension)(void);
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
void(*)(void*));
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
void(*)(void*), unsigned char);
int (*strglob)(const char*,const char*);
/* Version 3.8.11 and later */
sqlite3_value *(*value_dup)(const sqlite3_value*);
void (*value_free)(sqlite3_value*);
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
/* Version 3.9.0 and later */
unsigned int (*value_subtype)(sqlite3_value*);
void (*result_subtype)(sqlite3_context*,unsigned int);
/* Version 3.10.0 and later */
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
int (*strlike)(const char*,const char*,unsigned int);
int (*db_cacheflush)(sqlite3*);
/* Version 3.12.0 and later */
int (*system_errno)(sqlite3*);
/* Version 3.14.0 and later */
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
char *(*expanded_sql)(sqlite3_stmt*);
/* Version 3.18.0 and later */
void (*set_last_insert_rowid)(sqlite3*,sqlite3_int64);
/* Version 3.20.0 and later */
int (*prepare_v3)(sqlite3*,const char*,int,unsigned int,
sqlite3_stmt**,const char**);
int (*prepare16_v3)(sqlite3*,const void*,int,unsigned int,
sqlite3_stmt**,const void**);
int (*bind_pointer)(sqlite3_stmt*,int,void*,const char*,void(*)(void*));
void (*result_pointer)(sqlite3_context*,void*,const char*,void(*)(void*));
void *(*value_pointer)(sqlite3_value*,const char*);
};
/*
** This is the function signature used for all extension entry points. It
** is also defined in the file "loadext.c".
*/
typedef int (*sqlite3_loadext_entry)(
sqlite3 *db, /* Handle to the database. */
char **pzErrMsg, /* Used to set error string on failure. */
const sqlite3_api_routines *pThunk /* Extension API function pointers. */
);
/*
** The following macros redefine the API routines so that they are
** redirected through the global sqlite3_api structure.
**
** This header file is also used by the loadext.c source file
** (part of the main SQLite library - not an extension) so that
** it can get access to the sqlite3_api_routines structure
** definition. But the main library does not want to redefine
** the API. So the redefinition macros are only valid if the
** SQLITE_CORE macros is undefined.
*/
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
#endif
#define sqlite3_bind_blob sqlite3_api->bind_blob
#define sqlite3_bind_double sqlite3_api->bind_double
#define sqlite3_bind_int sqlite3_api->bind_int
#define sqlite3_bind_int64 sqlite3_api->bind_int64
#define sqlite3_bind_null sqlite3_api->bind_null
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
#define sqlite3_bind_text sqlite3_api->bind_text
#define sqlite3_bind_text16 sqlite3_api->bind_text16
#define sqlite3_bind_value sqlite3_api->bind_value
#define sqlite3_busy_handler sqlite3_api->busy_handler
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
#define sqlite3_changes sqlite3_api->changes
#define sqlite3_close sqlite3_api->close
#define sqlite3_collation_needed sqlite3_api->collation_needed
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
#define sqlite3_column_blob sqlite3_api->column_blob
#define sqlite3_column_bytes sqlite3_api->column_bytes
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
#define sqlite3_column_count sqlite3_api->column_count
#define sqlite3_column_database_name sqlite3_api->column_database_name
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
#define sqlite3_column_decltype sqlite3_api->column_decltype
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
#define sqlite3_column_double sqlite3_api->column_double
#define sqlite3_column_int sqlite3_api->column_int
#define sqlite3_column_int64 sqlite3_api->column_int64
#define sqlite3_column_name sqlite3_api->column_name
#define sqlite3_column_name16 sqlite3_api->column_name16
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
#define sqlite3_column_table_name sqlite3_api->column_table_name
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
#define sqlite3_column_text sqlite3_api->column_text
#define sqlite3_column_text16 sqlite3_api->column_text16
#define sqlite3_column_type sqlite3_api->column_type
#define sqlite3_column_value sqlite3_api->column_value
#define sqlite3_commit_hook sqlite3_api->commit_hook
#define sqlite3_complete sqlite3_api->complete
#define sqlite3_complete16 sqlite3_api->complete16
#define sqlite3_create_collation sqlite3_api->create_collation
#define sqlite3_create_collation16 sqlite3_api->create_collation16
#define sqlite3_create_function sqlite3_api->create_function
#define sqlite3_create_function16 sqlite3_api->create_function16
#define sqlite3_create_module sqlite3_api->create_module
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
#define sqlite3_data_count sqlite3_api->data_count
#define sqlite3_db_handle sqlite3_api->db_handle
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
#define sqlite3_errcode sqlite3_api->errcode
#define sqlite3_errmsg sqlite3_api->errmsg
#define sqlite3_errmsg16 sqlite3_api->errmsg16
#define sqlite3_exec sqlite3_api->exec
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_expired sqlite3_api->expired
#endif
#define sqlite3_finalize sqlite3_api->finalize
#define sqlite3_free sqlite3_api->free
#define sqlite3_free_table sqlite3_api->free_table
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
#define sqlite3_get_table sqlite3_api->get_table
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_global_recover sqlite3_api->global_recover
#endif
#define sqlite3_interrupt sqlite3_api->interruptx
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
#define sqlite3_libversion sqlite3_api->libversion
#define sqlite3_libversion_number sqlite3_api->libversion_number
#define sqlite3_malloc sqlite3_api->malloc
#define sqlite3_mprintf sqlite3_api->mprintf
#define sqlite3_open sqlite3_api->open
#define sqlite3_open16 sqlite3_api->open16
#define sqlite3_prepare sqlite3_api->prepare
#define sqlite3_prepare16 sqlite3_api->prepare16
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_profile sqlite3_api->profile
#define sqlite3_progress_handler sqlite3_api->progress_handler
#define sqlite3_realloc sqlite3_api->realloc
#define sqlite3_reset sqlite3_api->reset
#define sqlite3_result_blob sqlite3_api->result_blob
#define sqlite3_result_double sqlite3_api->result_double
#define sqlite3_result_error sqlite3_api->result_error
#define sqlite3_result_error16 sqlite3_api->result_error16
#define sqlite3_result_int sqlite3_api->result_int
#define sqlite3_result_int64 sqlite3_api->result_int64
#define sqlite3_result_null sqlite3_api->result_null
#define sqlite3_result_text sqlite3_api->result_text
#define sqlite3_result_text16 sqlite3_api->result_text16
#define sqlite3_result_text16be sqlite3_api->result_text16be
#define sqlite3_result_text16le sqlite3_api->result_text16le
#define sqlite3_result_value sqlite3_api->result_value
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
#define sqlite3_snprintf sqlite3_api->snprintf
#define sqlite3_step sqlite3_api->step
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
#define sqlite3_total_changes sqlite3_api->total_changes
#define sqlite3_trace sqlite3_api->trace
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
#endif
#define sqlite3_update_hook sqlite3_api->update_hook
#define sqlite3_user_data sqlite3_api->user_data
#define sqlite3_value_blob sqlite3_api->value_blob
#define sqlite3_value_bytes sqlite3_api->value_bytes
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
#define sqlite3_value_double sqlite3_api->value_double
#define sqlite3_value_int sqlite3_api->value_int
#define sqlite3_value_int64 sqlite3_api->value_int64
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
#define sqlite3_value_text sqlite3_api->value_text
#define sqlite3_value_text16 sqlite3_api->value_text16
#define sqlite3_value_text16be sqlite3_api->value_text16be
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
#define sqlite3_vsnprintf sqlite3_api->vsnprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
#define sqlite3_blob_close sqlite3_api->blob_close
#define sqlite3_blob_open sqlite3_api->blob_open
#define sqlite3_blob_read sqlite3_api->blob_read
#define sqlite3_blob_write sqlite3_api->blob_write
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
#define sqlite3_file_control sqlite3_api->file_control
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
#define sqlite3_memory_used sqlite3_api->memory_used
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
#define sqlite3_mutex_free sqlite3_api->mutex_free
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
#define sqlite3_mutex_try sqlite3_api->mutex_try
#define sqlite3_open_v2 sqlite3_api->open_v2
#define sqlite3_release_memory sqlite3_api->release_memory
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
#define sqlite3_sleep sqlite3_api->sleep
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
#define sqlite3_vfs_find sqlite3_api->vfs_find
#define sqlite3_vfs_register sqlite3_api->vfs_register
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
#define sqlite3_result_error_code sqlite3_api->result_error_code
#define sqlite3_test_control sqlite3_api->test_control
#define sqlite3_randomness sqlite3_api->randomness
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
#define sqlite3_limit sqlite3_api->limit
#define sqlite3_next_stmt sqlite3_api->next_stmt
#define sqlite3_sql sqlite3_api->sql
#define sqlite3_status sqlite3_api->status
#define sqlite3_backup_finish sqlite3_api->backup_finish
#define sqlite3_backup_init sqlite3_api->backup_init
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
#define sqlite3_backup_step sqlite3_api->backup_step
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
#define sqlite3_db_config sqlite3_api->db_config
#define sqlite3_db_mutex sqlite3_api->db_mutex
#define sqlite3_db_status sqlite3_api->db_status
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
#define sqlite3_log sqlite3_api->log
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
#define sqlite3_sourceid sqlite3_api->sourceid
#define sqlite3_stmt_status sqlite3_api->stmt_status
#define sqlite3_strnicmp sqlite3_api->strnicmp
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
#define sqlite3_wal_hook sqlite3_api->wal_hook
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
#define sqlite3_vtab_config sqlite3_api->vtab_config
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
/* Version 3.7.16 and later */
#define sqlite3_close_v2 sqlite3_api->close_v2
#define sqlite3_db_filename sqlite3_api->db_filename
#define sqlite3_db_readonly sqlite3_api->db_readonly
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
#define sqlite3_errstr sqlite3_api->errstr
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
#define sqlite3_stricmp sqlite3_api->stricmp
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
#define sqlite3_uri_int64 sqlite3_api->uri_int64
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
/* Version 3.8.7 and later */
#define sqlite3_auto_extension sqlite3_api->auto_extension
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
#define sqlite3_bind_text64 sqlite3_api->bind_text64
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
#define sqlite3_load_extension sqlite3_api->load_extension
#define sqlite3_malloc64 sqlite3_api->malloc64
#define sqlite3_msize sqlite3_api->msize
#define sqlite3_realloc64 sqlite3_api->realloc64
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
#define sqlite3_result_blob64 sqlite3_api->result_blob64
#define sqlite3_result_text64 sqlite3_api->result_text64
#define sqlite3_strglob sqlite3_api->strglob
/* Version 3.8.11 and later */
#define sqlite3_value_dup sqlite3_api->value_dup
#define sqlite3_value_free sqlite3_api->value_free
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
/* Version 3.9.0 and later */
#define sqlite3_value_subtype sqlite3_api->value_subtype
#define sqlite3_result_subtype sqlite3_api->result_subtype
/* Version 3.10.0 and later */
#define sqlite3_status64 sqlite3_api->status64
#define sqlite3_strlike sqlite3_api->strlike
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
/* Version 3.12.0 and later */
#define sqlite3_system_errno sqlite3_api->system_errno
/* Version 3.14.0 and later */
#define sqlite3_trace_v2 sqlite3_api->trace_v2
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
/* Version 3.18.0 and later */
#define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid
/* Version 3.20.0 and later */
#define sqlite3_prepare_v3 sqlite3_api->prepare_v3
#define sqlite3_prepare16_v3 sqlite3_api->prepare16_v3
#define sqlite3_bind_pointer sqlite3_api->bind_pointer
#define sqlite3_result_pointer sqlite3_api->result_pointer
#define sqlite3_value_pointer sqlite3_api->value_pointer
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
/* This case when the file really is being compiled as a loadable
** extension */
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
# define SQLITE_EXTENSION_INIT3 \
extern const sqlite3_api_routines *sqlite3_api;
#else
/* This case when the file is being statically linked into the
** application */
# define SQLITE_EXTENSION_INIT1 /*no-op*/
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
# define SQLITE_EXTENSION_INIT3 /*no-op*/
#endif
#endif /* SQLITE3EXT_H */

View File

@ -0,0 +1,620 @@
// sqlite3pp.cpp
//
// The MIT License
//
// Copyright (c) 2015 Wongoo Lee (iwongu at gmail dot com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <cstring>
#include <memory>
#include "sqlite3pp.h"
namespace sqlite3pp
{
null_type ignore;
namespace
{
int busy_handler_impl(void* p, int cnt)
{
auto h = static_cast<database::busy_handler*>(p);
return (*h)(cnt);
}
int commit_hook_impl(void* p)
{
auto h = static_cast<database::commit_handler*>(p);
return (*h)();
}
void rollback_hook_impl(void* p)
{
auto h = static_cast<database::rollback_handler*>(p);
(*h)();
}
void update_hook_impl(void* p, int opcode, char const* dbname, char const* tablename, long long int rowid)
{
auto h = static_cast<database::update_handler*>(p);
(*h)(opcode, dbname, tablename, rowid);
}
int authorizer_impl(void* p, int evcode, char const* p1, char const* p2, char const* dbname, char const* tvname)
{
auto h = static_cast<database::authorize_handler*>(p);
return (*h)(evcode, p1, p2, dbname, tvname);
}
} // namespace
database::database(char const* dbname, int flags, char const* vfs) : db_(nullptr), borrowing_(false)
{
if (dbname) {
auto rc = connect(dbname, flags, vfs);
if (rc != SQLITE_OK)
throw database_error("can't connect database");
}
}
database::database(sqlite3* pdb) : db_(pdb), borrowing_(true)
{
}
database::database(database&& db) : db_(std::move(db.db_)),
borrowing_(std::move(db.borrowing_)),
bh_(std::move(db.bh_)),
ch_(std::move(db.ch_)),
rh_(std::move(db.rh_)),
uh_(std::move(db.uh_)),
ah_(std::move(db.ah_))
{
db.db_ = nullptr;
}
database& database::operator=(database&& db)
{
db_ = std::move(db.db_);
db.db_ = nullptr;
borrowing_ = std::move(db.borrowing_);
bh_ = std::move(db.bh_);
ch_ = std::move(db.ch_);
rh_ = std::move(db.rh_);
uh_ = std::move(db.uh_);
ah_ = std::move(db.ah_);
return *this;
}
database::~database()
{
if (!borrowing_) {
disconnect();
}
}
int database::connect(char const* dbname, int flags, char const* vfs)
{
if (!borrowing_) {
disconnect();
}
return sqlite3_open_v2(dbname, &db_, flags, vfs);
}
int database::disconnect()
{
auto rc = SQLITE_OK;
if (db_) {
rc = sqlite3_close(db_);
if (rc == SQLITE_OK) {
db_ = nullptr;
}
}
return rc;
}
int database::attach(char const* dbname, char const* name)
{
return executef("ATTACH '%q' AS '%q'", dbname, name);
}
int database::detach(char const* name)
{
return executef("DETACH '%q'", name);
}
int database::backup(database& destdb, backup_handler h)
{
return backup("main", destdb, "main", h);
}
int database::backup(char const* dbname, database& destdb, char const* destdbname, backup_handler h, int step_page)
{
sqlite3_backup* bkup = sqlite3_backup_init(destdb.db_, destdbname, db_, dbname);
if (!bkup) {
return error_code();
}
auto rc = SQLITE_OK;
do {
rc = sqlite3_backup_step(bkup, step_page);
if (h) {
h(sqlite3_backup_remaining(bkup), sqlite3_backup_pagecount(bkup), rc);
}
} while (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED);
sqlite3_backup_finish(bkup);
return rc;
}
void database::set_busy_handler(busy_handler h)
{
bh_ = h;
sqlite3_busy_handler(db_, bh_ ? busy_handler_impl : 0, &bh_);
}
void database::set_commit_handler(commit_handler h)
{
ch_ = h;
sqlite3_commit_hook(db_, ch_ ? commit_hook_impl : 0, &ch_);
}
void database::set_rollback_handler(rollback_handler h)
{
rh_ = h;
sqlite3_rollback_hook(db_, rh_ ? rollback_hook_impl : 0, &rh_);
}
void database::set_update_handler(update_handler h)
{
uh_ = h;
sqlite3_update_hook(db_, uh_ ? update_hook_impl : 0, &uh_);
}
void database::set_authorize_handler(authorize_handler h)
{
ah_ = h;
sqlite3_set_authorizer(db_, ah_ ? authorizer_impl : 0, &ah_);
}
long long int database::last_insert_rowid() const
{
return sqlite3_last_insert_rowid(db_);
}
int database::enable_foreign_keys(bool enable)
{
return sqlite3_db_config(db_, SQLITE_DBCONFIG_ENABLE_FKEY, enable ? 1 : 0, nullptr);
}
int database::enable_triggers(bool enable)
{
return sqlite3_db_config(db_, SQLITE_DBCONFIG_ENABLE_TRIGGER, enable ? 1 : 0, nullptr);
}
int database::enable_extended_result_codes(bool enable)
{
return sqlite3_extended_result_codes(db_, enable ? 1 : 0);
}
int database::changes() const
{
return sqlite3_changes(db_);
}
int database::error_code() const
{
return sqlite3_errcode(db_);
}
int database::extended_error_code() const
{
return sqlite3_extended_errcode(db_);
}
char const* database::error_msg() const
{
return sqlite3_errmsg(db_);
}
int database::execute(char const* sql)
{
return sqlite3_exec(db_, sql, 0, 0, 0);
}
int database::executef(char const* sql, ...)
{
va_list ap;
va_start(ap, sql);
std::shared_ptr<char> msql(sqlite3_vmprintf(sql, ap), sqlite3_free);
va_end(ap);
return execute(msql.get());
}
int database::set_busy_timeout(int ms)
{
return sqlite3_busy_timeout(db_, ms);
}
statement::statement(database& db, char const* stmt) : db_(db), stmt_(0), tail_(0)
{
if (stmt) {
auto rc = prepare(stmt);
if (rc != SQLITE_OK)
throw database_error(db_);
}
}
statement::~statement()
{
// finish() can return error. If you want to check the error, call
// finish() explicitly before this object is destructed.
finish();
}
int statement::prepare(char const* stmt)
{
auto rc = finish();
if (rc != SQLITE_OK)
return rc;
return prepare_impl(stmt);
}
int statement::prepare_impl(char const* stmt)
{
return sqlite3_prepare_v2(db_.db_, stmt, std::strlen(stmt), &stmt_, &tail_);
}
int statement::finish()
{
auto rc = SQLITE_OK;
if (stmt_) {
rc = finish_impl(stmt_);
stmt_ = nullptr;
}
tail_ = nullptr;
return rc;
}
int statement::finish_impl(sqlite3_stmt* stmt)
{
return sqlite3_finalize(stmt);
}
int statement::step()
{
return sqlite3_step(stmt_);
}
int statement::reset()
{
return sqlite3_reset(stmt_);
}
int statement::bind(int idx, int value)
{
return sqlite3_bind_int(stmt_, idx, value);
}
int statement::bind(int idx, double value)
{
return sqlite3_bind_double(stmt_, idx, value);
}
int statement::bind(int idx, long long int value)
{
return sqlite3_bind_int64(stmt_, idx, value);
}
int statement::bind(int idx, char const* value, copy_semantic fcopy)
{
return sqlite3_bind_text(stmt_, idx, value, std::strlen(value), fcopy == copy ? SQLITE_TRANSIENT : SQLITE_STATIC );
}
int statement::bind(int idx, void const* value, int n, copy_semantic fcopy)
{
return sqlite3_bind_blob(stmt_, idx, value, n, fcopy == copy ? SQLITE_TRANSIENT : SQLITE_STATIC );
}
int statement::bind(int idx, std::string const& value, copy_semantic fcopy)
{
return sqlite3_bind_text(stmt_, idx, value.c_str(), value.size(), fcopy == copy ? SQLITE_TRANSIENT : SQLITE_STATIC );
}
int statement::bind(int idx)
{
return sqlite3_bind_null(stmt_, idx);
}
int statement::bind(int idx, null_type)
{
return bind(idx);
}
int statement::bind(char const* name, int value)
{
auto idx = sqlite3_bind_parameter_index(stmt_, name);
return bind(idx, value);
}
int statement::bind(char const* name, double value)
{
auto idx = sqlite3_bind_parameter_index(stmt_, name);
return bind(idx, value);
}
int statement::bind(char const* name, long long int value)
{
auto idx = sqlite3_bind_parameter_index(stmt_, name);
return bind(idx, value);
}
int statement::bind(char const* name, char const* value, copy_semantic fcopy)
{
auto idx = sqlite3_bind_parameter_index(stmt_, name);
return bind(idx, value, fcopy);
}
int statement::bind(char const* name, void const* value, int n, copy_semantic fcopy)
{
auto idx = sqlite3_bind_parameter_index(stmt_, name);
return bind(idx, value, n, fcopy);
}
int statement::bind(char const* name, std::string const& value, copy_semantic fcopy)
{
auto idx = sqlite3_bind_parameter_index(stmt_, name);
return bind(idx, value, fcopy);
}
int statement::bind(char const* name)
{
auto idx = sqlite3_bind_parameter_index(stmt_, name);
return bind(idx);
}
int statement::bind(char const* name, null_type)
{
return bind(name);
}
command::bindstream::bindstream(command& cmd, int idx) : cmd_(cmd), idx_(idx)
{
}
command::command(database& db, char const* stmt) : statement(db, stmt)
{
}
command::bindstream command::binder(int idx)
{
return bindstream(*this, idx);
}
int command::execute()
{
auto rc = step();
if (rc == SQLITE_DONE) rc = SQLITE_OK;
return rc;
}
int command::execute_all()
{
auto rc = execute();
if (rc != SQLITE_OK) return rc;
char const* sql = tail_;
while (std::strlen(sql) > 0) { // sqlite3_complete() is broken.
sqlite3_stmt* old_stmt = stmt_;
if ((rc = prepare_impl(sql)) != SQLITE_OK) return rc;
if ((rc = sqlite3_transfer_bindings(old_stmt, stmt_)) != SQLITE_OK) return rc;
finish_impl(old_stmt);
if ((rc = execute()) != SQLITE_OK) return rc;
sql = tail_;
}
return rc;
}
query::rows::getstream::getstream(rows* rws, int idx) : rws_(rws), idx_(idx)
{
}
query::rows::rows(sqlite3_stmt* stmt) : stmt_(stmt)
{
}
int query::rows::data_count() const
{
return sqlite3_data_count(stmt_);
}
int query::rows::column_type(int idx) const
{
return sqlite3_column_type(stmt_, idx);
}
int query::rows::column_bytes(int idx) const
{
return sqlite3_column_bytes(stmt_, idx);
}
int query::rows::get(int idx, int) const
{
return sqlite3_column_int(stmt_, idx);
}
double query::rows::get(int idx, double) const
{
return sqlite3_column_double(stmt_, idx);
}
long long int query::rows::get(int idx, long long int) const
{
return sqlite3_column_int64(stmt_, idx);
}
char const* query::rows::get(int idx, char const*) const
{
return reinterpret_cast<char const*>(sqlite3_column_text(stmt_, idx));
}
std::string query::rows::get(int idx, std::string) const
{
return get(idx, (char const*)0);
}
void const* query::rows::get(int idx, void const*) const
{
return sqlite3_column_blob(stmt_, idx);
}
null_type query::rows::get(int /*idx*/, null_type) const
{
return ignore;
}
query::rows::getstream query::rows::getter(int idx)
{
return getstream(this, idx);
}
query::query_iterator::query_iterator() : cmd_(0)
{
rc_ = SQLITE_DONE;
}
query::query_iterator::query_iterator(query* cmd) : cmd_(cmd)
{
rc_ = cmd_->step();
if (rc_ != SQLITE_ROW && rc_ != SQLITE_DONE)
throw database_error(cmd_->db_);
}
bool query::query_iterator::operator==(query::query_iterator const& other) const
{
return rc_ == other.rc_;
}
bool query::query_iterator::operator!=(query::query_iterator const& other) const
{
return rc_ != other.rc_;
}
query::query_iterator& query::query_iterator::operator++()
{
rc_ = cmd_->step();
if (rc_ != SQLITE_ROW && rc_ != SQLITE_DONE)
throw database_error(cmd_->db_);
return *this;
}
query::query_iterator::value_type query::query_iterator::operator*() const
{
return rows(cmd_->stmt_);
}
query::query(database& db, char const* stmt) : statement(db, stmt)
{
}
int query::column_count() const
{
return sqlite3_column_count(stmt_);
}
char const* query::column_name(int idx) const
{
return sqlite3_column_name(stmt_, idx);
}
char const* query::column_decltype(int idx) const
{
return sqlite3_column_decltype(stmt_, idx);
}
query::iterator query::begin()
{
return query_iterator(this);
}
query::iterator query::end()
{
return query_iterator();
}
transaction::transaction(database& db, bool fcommit, bool freserve) : db_(&db), fcommit_(fcommit)
{
int rc = db_->execute(freserve ? "BEGIN IMMEDIATE" : "BEGIN");
if (rc != SQLITE_OK)
throw database_error(*db_);
}
transaction::~transaction()
{
if (db_) {
// execute() can return error. If you want to check the error,
// call commit() or rollback() explicitly before this object is
// destructed.
db_->execute(fcommit_ ? "COMMIT" : "ROLLBACK");
}
}
int transaction::commit()
{
auto db = db_;
db_ = nullptr;
int rc = db->execute("COMMIT");
return rc;
}
int transaction::rollback()
{
auto db = db_;
db_ = nullptr;
int rc = db->execute("ROLLBACK");
return rc;
}
database_error::database_error(char const* msg) : std::runtime_error(msg)
{
}
database_error::database_error(database& db) : std::runtime_error(sqlite3_errmsg(db.db_))
{
}
} // namespace sqlite3pp

View File

@ -0,0 +1,345 @@
// sqlite3pp.h
//
// The MIT License
//
// Copyright (c) 2015 Wongoo Lee (iwongu at gmail dot com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef SQLITE3PP_H
#define SQLITE3PP_H
#define SQLITE3PP_VERSION "1.0.8"
#define SQLITE3PP_VERSION_MAJOR 1
#define SQLITE3PP_VERSION_MINOR 0
#define SQLITE3PP_VERSION_PATCH 8
#include <functional>
#include <iterator>
#include <stdexcept>
#include <string>
#include <tuple>
#ifdef SQLITE3PP_LOADABLE_EXTENSION
#include <sqlite3ext.h>
SQLITE_EXTENSION_INIT1
#else
# include <sqlite3.h>
#endif
namespace sqlite3pp
{
class database;
namespace ext
{
class function;
class aggregate;
database borrow(sqlite3* pdb);
}
template <class T>
struct convert {
using to_int = int;
};
class null_type {};
extern null_type ignore;
class noncopyable
{
protected:
noncopyable() = default;
~noncopyable() = default;
noncopyable(noncopyable&&) = default;
noncopyable& operator=(noncopyable&&) = default;
noncopyable(noncopyable const&) = delete;
noncopyable& operator=(noncopyable const&) = delete;
};
class database : noncopyable
{
friend class statement;
friend class database_error;
friend class ext::function;
friend class ext::aggregate;
friend database ext::borrow(sqlite3* pdb);
public:
using busy_handler = std::function<int (int)>;
using commit_handler = std::function<int ()>;
using rollback_handler = std::function<void ()>;
using update_handler = std::function<void (int, char const*, char const*, long long int)>;
using authorize_handler = std::function<int (int, char const*, char const*, char const*, char const*)>;
using backup_handler = std::function<void (int, int, int)>;
explicit database(char const* dbname = nullptr, int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, const char* vfs = nullptr);
database(database&& db);
database& operator=(database&& db);
~database();
int connect(char const* dbname, int flags, const char* vfs = nullptr);
int disconnect();
int attach(char const* dbname, char const* name);
int detach(char const* name);
int backup(database& destdb, backup_handler h = {});
int backup(char const* dbname, database& destdb, char const* destdbname, backup_handler h, int step_page = 5);
long long int last_insert_rowid() const;
int enable_foreign_keys(bool enable = true);
int enable_triggers(bool enable = true);
int enable_extended_result_codes(bool enable = true);
int changes() const;
int error_code() const;
int extended_error_code() const;
char const* error_msg() const;
int execute(char const* sql);
int executef(char const* sql, ...);
int set_busy_timeout(int ms);
void set_busy_handler(busy_handler h);
void set_commit_handler(commit_handler h);
void set_rollback_handler(rollback_handler h);
void set_update_handler(update_handler h);
void set_authorize_handler(authorize_handler h);
private:
database(sqlite3* pdb);
private:
sqlite3* db_;
bool borrowing_;
busy_handler bh_;
commit_handler ch_;
rollback_handler rh_;
update_handler uh_;
authorize_handler ah_;
};
class database_error : public std::runtime_error
{
public:
explicit database_error(char const* msg);
explicit database_error(database& db);
};
enum copy_semantic { copy, nocopy };
class statement : noncopyable
{
public:
int prepare(char const* stmt);
int finish();
int bind(int idx, int value);
int bind(int idx, double value);
int bind(int idx, long long int value);
int bind(int idx, char const* value, copy_semantic fcopy);
int bind(int idx, void const* value, int n, copy_semantic fcopy);
int bind(int idx, std::string const& value, copy_semantic fcopy);
int bind(int idx);
int bind(int idx, null_type);
int bind(char const* name, int value);
int bind(char const* name, double value);
int bind(char const* name, long long int value);
int bind(char const* name, char const* value, copy_semantic fcopy);
int bind(char const* name, void const* value, int n, copy_semantic fcopy);
int bind(char const* name, std::string const& value, copy_semantic fcopy);
int bind(char const* name);
int bind(char const* name, null_type);
int step();
int reset();
protected:
explicit statement(database& db, char const* stmt = nullptr);
~statement();
int prepare_impl(char const* stmt);
int finish_impl(sqlite3_stmt* stmt);
protected:
database& db_;
sqlite3_stmt* stmt_;
char const* tail_;
};
class command : public statement
{
public:
class bindstream
{
public:
bindstream(command& cmd, int idx);
template <class T>
bindstream& operator << (T value) {
auto rc = cmd_.bind(idx_, value);
if (rc != SQLITE_OK) {
throw database_error(cmd_.db_);
}
++idx_;
return *this;
}
bindstream& operator << (char const* value) {
auto rc = cmd_.bind(idx_, value, copy);
if (rc != SQLITE_OK) {
throw database_error(cmd_.db_);
}
++idx_;
return *this;
}
bindstream& operator << (std::string const& value) {
auto rc = cmd_.bind(idx_, value, copy);
if (rc != SQLITE_OK) {
throw database_error(cmd_.db_);
}
++idx_;
return *this;
}
private:
command& cmd_;
int idx_;
};
explicit command(database& db, char const* stmt = nullptr);
bindstream binder(int idx = 1);
int execute();
int execute_all();
};
class query : public statement
{
public:
class rows
{
public:
class getstream
{
public:
getstream(rows* rws, int idx);
template <class T>
getstream& operator >> (T& value) {
value = rws_->get(idx_, T());
++idx_;
return *this;
}
private:
rows* rws_;
int idx_;
};
explicit rows(sqlite3_stmt* stmt);
int data_count() const;
int column_type(int idx) const;
int column_bytes(int idx) const;
template <class T> T get(int idx) const {
return get(idx, T());
}
template <class... Ts>
std::tuple<Ts...> get_columns(typename convert<Ts>::to_int... idxs) const {
return std::make_tuple(get(idxs, Ts())...);
}
getstream getter(int idx = 0);
private:
int get(int idx, int) const;
double get(int idx, double) const;
long long int get(int idx, long long int) const;
char const* get(int idx, char const*) const;
std::string get(int idx, std::string) const;
void const* get(int idx, void const*) const;
null_type get(int idx, null_type) const;
private:
sqlite3_stmt* stmt_;
};
class query_iterator
: public std::iterator<std::input_iterator_tag, rows>
{
public:
query_iterator();
explicit query_iterator(query* cmd);
bool operator==(query_iterator const&) const;
bool operator!=(query_iterator const&) const;
query_iterator& operator++();
value_type operator*() const;
private:
query* cmd_;
int rc_;
};
explicit query(database& db, char const* stmt = nullptr);
int column_count() const;
char const* column_name(int idx) const;
char const* column_decltype(int idx) const;
using iterator = query_iterator;
iterator begin();
iterator end();
};
class transaction : noncopyable
{
public:
explicit transaction(database& db, bool fcommit = false, bool freserve = false);
~transaction();
int commit();
int rollback();
private:
database* db_;
bool fcommit_;
};
} // namespace sqlite3pp
#endif

View File

@ -0,0 +1,200 @@
// sqlite3ppext.cpp
//
// The MIT License
//
// Copyright (c) 2015 Wongoo Lee (iwongu at gmail dot com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <cstring>
#include "sqlite3ppext.h"
namespace sqlite3pp
{
namespace ext
{
namespace
{
void function_impl(sqlite3_context* ctx, int nargs, sqlite3_value** values)
{
auto f = static_cast<function::function_handler*>(sqlite3_user_data(ctx));
context c(ctx, nargs, values);
(*f)(c);
}
void step_impl(sqlite3_context* ctx, int nargs, sqlite3_value** values)
{
auto p = static_cast<std::pair<aggregate::pfunction_base, aggregate::pfunction_base>*>(sqlite3_user_data(ctx));
auto s = static_cast<aggregate::function_handler*>((*p).first.get());
context c(ctx, nargs, values);
((function::function_handler&)*s)(c);
}
void finalize_impl(sqlite3_context* ctx)
{
auto p = static_cast<std::pair<aggregate::pfunction_base, aggregate::pfunction_base>*>(sqlite3_user_data(ctx));
auto f = static_cast<aggregate::function_handler*>((*p).second.get());
context c(ctx);
((function::function_handler&)*f)(c);
}
} // namespace
database borrow(sqlite3* pdb) {
return database(pdb);
}
context::context(sqlite3_context* ctx, int nargs, sqlite3_value** values)
: ctx_(ctx), nargs_(nargs), values_(values)
{
}
int context::args_count() const
{
return nargs_;
}
int context::args_bytes(int idx) const
{
return sqlite3_value_bytes(values_[idx]);
}
int context::args_type(int idx) const
{
return sqlite3_value_type(values_[idx]);
}
int context::get(int idx, int) const
{
return sqlite3_value_int(values_[idx]);
}
double context::get(int idx, double) const
{
return sqlite3_value_double(values_[idx]);
}
long long int context::get(int idx, long long int) const
{
return sqlite3_value_int64(values_[idx]);
}
char const* context::get(int idx, char const*) const
{
return reinterpret_cast<char const*>(sqlite3_value_text(values_[idx]));
}
std::string context::get(int idx, std::string) const
{
return get(idx, (char const*)0);
}
void const* context::get(int idx, void const*) const
{
return sqlite3_value_blob(values_[idx]);
}
void context::result(int value)
{
sqlite3_result_int(ctx_, value);
}
void context::result(double value)
{
sqlite3_result_double(ctx_, value);
}
void context::result(long long int value)
{
sqlite3_result_int64(ctx_, value);
}
void context::result(std::string const& value)
{
result(value.c_str(), false);
}
void context::result(char const* value, bool fcopy)
{
sqlite3_result_text(ctx_, value, std::strlen(value), fcopy ? SQLITE_TRANSIENT : SQLITE_STATIC);
}
void context::result(void const* value, int n, bool fcopy)
{
sqlite3_result_blob(ctx_, value, n, fcopy ? SQLITE_TRANSIENT : SQLITE_STATIC );
}
void context::result()
{
sqlite3_result_null(ctx_);
}
void context::result(null_type)
{
sqlite3_result_null(ctx_);
}
void context::result_copy(int idx)
{
sqlite3_result_value(ctx_, values_[idx]);
}
void context::result_error(char const* msg)
{
sqlite3_result_error(ctx_, msg, std::strlen(msg));
}
void* context::aggregate_data(int size)
{
return sqlite3_aggregate_context(ctx_, size);
}
int context::aggregate_count()
{
return sqlite3_aggregate_count(ctx_);
}
function::function(database& db) : db_(db.db_)
{
}
int function::create(char const* name, function_handler h, int nargs)
{
fh_[name] = pfunction_base(new function_handler(h));
return sqlite3_create_function(db_, name, nargs, SQLITE_UTF8, fh_[name].get(), function_impl, 0, 0);
}
aggregate::aggregate(database& db) : db_(db.db_)
{
}
int aggregate::create(char const* name, function_handler s, function_handler f, int nargs)
{
ah_[name] = std::make_pair(pfunction_base(new function_handler(s)), pfunction_base(new function_handler(f)));
return sqlite3_create_function(db_, name, nargs, SQLITE_UTF8, &ah_[name], 0, step_impl, finalize_impl);
}
} // namespace ext
} // namespace sqlite3pp

View File

@ -0,0 +1,232 @@
// sqlite3ppext.h
//
// The MIT License
//
// Copyright (c) 2015 Wongoo Lee (iwongu at gmail dot com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef SQLITE3PPEXT_H
#define SQLITE3PPEXT_H
#include <cstddef>
#include <map>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include "sqlite3pp.h"
namespace sqlite3pp
{
namespace
{
template<size_t N>
struct Apply {
template<typename F, typename T, typename... A>
static inline auto apply(F&& f, T&& t, A&&... a)
-> decltype(Apply<N-1>::apply(std::forward<F>(f),
std::forward<T>(t),
std::get<N-1>(std::forward<T>(t)),
std::forward<A>(a)...))
{
return Apply<N-1>::apply(std::forward<F>(f),
std::forward<T>(t),
std::get<N-1>(std::forward<T>(t)),
std::forward<A>(a)...);
}
};
template<>
struct Apply<0> {
template<typename F, typename T, typename... A>
static inline auto apply(F&& f, T&&, A&&... a)
-> decltype(std::forward<F>(f)(std::forward<A>(a)...))
{
return std::forward<F>(f)(std::forward<A>(a)...);
}
};
template<typename F, typename T>
inline auto apply(F&& f, T&& t)
-> decltype(Apply<std::tuple_size<typename std::decay<T>::type>::value>::apply(std::forward<F>(f), std::forward<T>(t)))
{
return Apply<std::tuple_size<typename std::decay<T>::type>::value>::apply(
std::forward<F>(f), std::forward<T>(t));
}
}
namespace ext
{
database borrow(sqlite3* pdb);
class context : noncopyable
{
public:
explicit context(sqlite3_context* ctx, int nargs = 0, sqlite3_value** values = nullptr);
int args_count() const;
int args_bytes(int idx) const;
int args_type(int idx) const;
template <class T> T get(int idx) const {
return get(idx, T());
}
void result(int value);
void result(double value);
void result(long long int value);
void result(std::string const& value);
void result(char const* value, bool fcopy);
void result(void const* value, int n, bool fcopy);
void result();
void result(null_type);
void result_copy(int idx);
void result_error(char const* msg);
void* aggregate_data(int size);
int aggregate_count();
template <class... Ts>
std::tuple<Ts...> to_tuple() {
return to_tuple_impl(0, *this, std::tuple<Ts...>());
}
private:
int get(int idx, int) const;
double get(int idx, double) const;
long long int get(int idx, long long int) const;
char const* get(int idx, char const*) const;
std::string get(int idx, std::string) const;
void const* get(int idx, void const*) const;
template<class H, class... Ts>
static inline std::tuple<H, Ts...> to_tuple_impl(int index, const context& c, std::tuple<H, Ts...>&&)
{
auto h = std::make_tuple(c.context::get<H>(index));
return std::tuple_cat(h, to_tuple_impl(++index, c, std::tuple<Ts...>()));
}
static inline std::tuple<> to_tuple_impl(int /*index*/, const context& /*c*/, std::tuple<>&&)
{
return std::tuple<>();
}
private:
sqlite3_context* ctx_;
int nargs_;
sqlite3_value** values_;
};
namespace
{
template <class R, class... Ps>
void functionx_impl(sqlite3_context* ctx, int nargs, sqlite3_value** values)
{
context c(ctx, nargs, values);
auto f = static_cast<std::function<R (Ps...)>*>(sqlite3_user_data(ctx));
c.result(apply(*f, c.to_tuple<Ps...>()));
}
}
class function : noncopyable
{
public:
using function_handler = std::function<void (context&)>;
using pfunction_base = std::shared_ptr<void>;
explicit function(database& db);
int create(char const* name, function_handler h, int nargs = 0);
template <class F> int create(char const* name, std::function<F> h) {
fh_[name] = std::shared_ptr<void>(new std::function<F>(h));
return create_function_impl<F>()(db_, fh_[name].get(), name);
}
private:
template<class R, class... Ps>
struct create_function_impl;
template<class R, class... Ps>
struct create_function_impl<R (Ps...)>
{
int operator()(sqlite3* db, void* fh, char const* name) {
return sqlite3_create_function(db, name, sizeof...(Ps), SQLITE_UTF8, fh,
functionx_impl<R, Ps...>,
0, 0);
}
};
private:
sqlite3* db_;
std::map<std::string, pfunction_base> fh_;
};
namespace
{
template <class T, class... Ps>
void stepx_impl(sqlite3_context* ctx, int nargs, sqlite3_value** values)
{
context c(ctx, nargs, values);
T* t = static_cast<T*>(c.aggregate_data(sizeof(T)));
if (c.aggregate_count() == 1) new (t) T;
apply([](T* tt, Ps... ps){tt->step(ps...);},
std::tuple_cat(std::make_tuple(t), c.to_tuple<Ps...>()));
}
template <class T>
void finishN_impl(sqlite3_context* ctx)
{
context c(ctx);
T* t = static_cast<T*>(c.aggregate_data(sizeof(T)));
c.result(t->finish());
t->~T();
}
}
class aggregate : noncopyable
{
public:
using function_handler = std::function<void (context&)>;
using pfunction_base = std::shared_ptr<void>;
explicit aggregate(database& db);
int create(char const* name, function_handler s, function_handler f, int nargs = 1);
template <class T, class... Ps>
int create(char const* name) {
return sqlite3_create_function(db_, name, sizeof...(Ps), SQLITE_UTF8, 0, 0, stepx_impl<T, Ps...>, finishN_impl<T>);
}
private:
sqlite3* db_;
std::map<std::string, std::pair<pfunction_base, pfunction_base> > ah_;
};
} // namespace ext
} // namespace sqlite3pp
#endif

View File

@ -12,15 +12,27 @@ set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
#
set(ToolKit_Root ${CMAKE_SOURCE_DIR}/3rdpart/ZLToolKit/src)
set(SQLite_Root ${CMAKE_SOURCE_DIR}/3rdpart/SQLite/src)
set(SQLite3pp_Root ${CMAKE_SOURCE_DIR}/3rdpart/sqlite3pp)
set(jsoncpp_Root ${CMAKE_SOURCE_DIR}/3rdpart/jsoncpp)
set(MediaKit_Root ${CMAKE_SOURCE_DIR}/src)
#
INCLUDE_DIRECTORIES(${ToolKit_Root})
INCLUDE_DIRECTORIES(${MediaKit_Root})
INCLUDE_DIRECTORIES(${SQLite_Root})
#INCLUDE_DIRECTORIES(${SQLite3pp_Root})
INCLUDE_DIRECTORIES(3rdpart)
#
file(GLOB ToolKit_src_list ${ToolKit_Root}/*/*.cpp ${ToolKit_Root}/*/*.h ${ToolKit_Root}/*/*.c)
file(GLOB SQLite_src_list ${SQLite_Root}/*.cpp ${SQLite_Root}/*.h ${SQLite_Root}/*.c)
file(GLOB MediaKit_src_list ${MediaKit_Root}/*/*.cpp ${MediaKit_Root}/*/*.h ${MediaKit_Root}/*/*.c)
file(GLOB sqlite3pp_src_list ${SQLite3pp_Root}/*.cpp ${SQLite3pp_Root}/*.h)
file(GLOB jsoncpp_src_list ${jsoncpp_Root}/*.cpp ${jsoncpp_Root}/*.h )
add_library(sqlite3 STATIC ${SQLite_src_list})
add_library(sqlite3pp STATIC ${sqlite3pp_src_list})
add_library(jsoncpp STATIC ${jsoncpp_src_list})
#win32
if (NOT WIN32)
@ -42,9 +54,9 @@ if(ENABLE_HLS)
message(STATUS "ENABLE_HLS defined")
add_definitions(-DENABLE_HLS)
set(MediaServer_Root ${CMAKE_SOURCE_DIR}/3rdpart/media-server)
set(LINK_LIB_LIST zlmediakit zltoolkit mpeg)
set(LINK_LIB_LIST zlmediakit zltoolkit mpeg jsoncpp sqlite3pp sqlite3)
else()
set(LINK_LIB_LIST zlmediakit zltoolkit)
set(LINK_LIB_LIST zlmediakit zltoolkit jsoncpp sqlite3pp sqlite3)
endif()
#openssl
@ -96,6 +108,7 @@ endif ()
#
add_library(zltoolkit STATIC ${ToolKit_src_list})
add_library(zlmediakit STATIC ${MediaKit_src_list})
@ -121,8 +134,10 @@ endif ()
#
add_subdirectory(tests)
#add_subdirectory(tests)
#
add_subdirectory(server)

80
docker/Dockerfile Normal file
View File

@ -0,0 +1,80 @@
#################################################################
# 在项目根目录执行 docker build -t cl-zlmedia -f ./docker/Dockerfile .
#################################################################
#基础
FROM ajiva/ubuntu-ffmpeg4 AS base
WORKDIR /tmp/workdir
RUN sed -i s@/archive.ubuntu.com/@/mirrors.163.com/@g /etc/apt/sources.list
RUN apt-get update && \
apt-get -qqy install --no-install-recommends \
g++ \
make \
cmake \
git \
libmysqlclient-dev \
libssl-dev \
libx264-dev \
libfaac-dev \
libmp4v2-dev \
wget \
curl \
sqlite3 \
libsqlite3-dev \
nginx \
yasm && \
apt-get -qqy clean && \
rm -rf /var/lib/apt/lists/*
RUN rm -f /etc/nginx/sites-enabled/default
#后端
FROM base as build_b
WORKDIR /usr/src/ZLMediaKit
ADD 3rdpart ./3rdpart
ADD Android ./Android
ADD cmake ./cmake
ADD node_modules ./node_modules
ADD server ./server
ADD src ./src
ADD tests ./tests
ADD .travis.yml _config.yml CMakeLists.txt docker/build_for_docker_linux.sh ./
RUN chmod +x build_for_docker_linux.sh
RUN mkdir -p build/bin/dbdata && \
mkdir -p build/bin/log
RUN ["/bin/bash", "./build_for_docker_linux.sh"]
#前端
FROM base as build_f
ENV ZLMEDIAKIT_NVR_UI_VERSION=1.0.2
WORKDIR /usr/src/ZLMediaKitUI
RUN mkdir ui && \
curl -sLO https://github.com/chenxiaolei/ZLMediaKit_NVR_UI/releases/download/1.0.2/zlmediakit_nvr_ui.1.0.2.tar.gz &&\
tar -xvf zlmediakit_nvr_ui.1.0.2.tar.gz -C ui
#最终发布
FROM base AS release
MAINTAINER chenxiaolei <4854336@qq.com>
COPY --from=build_b /usr/src/ZLMediaKit/build /usr/src/ZLMediaKit/build
COPY --from=build_f /usr/src/ZLMediaKitUI /usr/src/ZLMediaKitUI
WORKDIR /usr/src/ZLMediaKit
COPY docker/nginx_zlmedia.conf /etc/nginx/conf.d/zlmedia.conf
COPY docker/docker-entrypoint.sh docker/zlmedia-ui-env.sh /usr/local/bin/
COPY docker/config_docker.ini /usr/src/ZLMediaKit/build/bin/config.ini
RUN chmod +x /usr/local/bin/docker-entrypoint.sh /usr/local/bin/zlmedia-ui-env.sh && \
ln -s /usr/src/ZLMediaKit/build/bin zldata
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
EXPOSE 1935 10800 554 80

View File

@ -0,0 +1,8 @@
#!/bin/bash
mkdir -p linux_build
rm -rf ./build
ln -s ./linux_build build
cd linux_build
cmake ..
make -j4

123
docker/config_docker.ini Normal file
View File

@ -0,0 +1,123 @@
; for docker
[api]
apiDebug=1
secret=123456
[ffmpeg]
bin=/usr/local/bin/ffmpeg
cmd=-i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s
log=/usr/src/ZLMediaKit/linux_build/bin/ffmpeg/ffmpeg.log
[general]
enableVhost=0
#流量汇报事件流量阈值,单位KB默认100MB
flowThreshold=102400
#等待流注册超时时间,收到播放器后请求后,如果未找到相关流,服务器会等待一定时间,
#如果在这个时间内,相关流注册上了,那么服务器会立即响应播放器播放成功,
#否则会最多等待kMaxStreamWaitTimeMS毫秒然后响应播放器播放失败
maxStreamWaitMS=8000
#流无人观看并且超过若干时间后才触发kBroadcastStreamNoneReader事件
#默认连续60秒无人观看然后触发
streamNoneReaderDelayMS=60000
[hls]
#HLS文件写缓存大小
fileBufSize=65536
#录制文件路径
filePath=/usr/src/ZLMediaKit/linux_build/bin/httpRoot
#HLS切片时长,单位秒
segDur=3
#HLS切片个数
segNum=6
[hook]
admin_params=secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc
enable=0
on_flow_report=https://127.0.0.1/index/hook/on_flow_report
on_http_access=https://127.0.0.1/index/hook/on_http_access
on_play=https://127.0.0.1/index/hook/on_play
on_publish=https://127.0.0.1/index/hook/on_publish
on_record_mp4=https://127.0.0.1/index/hook/on_record_mp4
on_rtsp_auth=https://127.0.0.1/index/hook/on_rtsp_auth
on_rtsp_realm=https://127.0.0.1/index/hook/on_rtsp_realm
on_shell_login=https://127.0.0.1/index/hook/on_shell_login
on_stream_changed=https://127.0.0.1/index/hook/on_stream_changed
on_stream_none_reader=https://127.0.0.1/index/hook/on_stream_none_reader
on_stream_not_found=https://127.0.0.1/index/hook/on_stream_not_found
timeoutSec=10
[http]
#http 字符编码
charSet=utf-8
#http keep-alive秒数
keepAliveSecond=10
#http keep-alive最大请求数
maxReqCount=100
#最大请求字节数
maxReqSize=104857600
#http 404错误提示内容
notFound=<html><head><title>404 Not Found</title></head><body bgcolor="white"><center><h1>您访问的资源不存在!</h1></center><hr><center>ZLMediaKit-4.0</center></body></html>
#http端口号
port=80
#http 服务器根目录
rootPath=/usr/src/ZLMediaKit/linux_build/bin/httpRoot
#http 文件发送缓存大小
sendBufSize=65536
sslport=443
[multicast]
#组播分配截止地址
addrMax=239.255.255.255
#组播分配起始地址
addrMin=239.0.0.0
#组播TTL
udpTTL=64
[record]
#查看录像的应用名称
appName=record
#录制文件路径
filePath=/usr/src/ZLMediaKit/linux_build/bin/httpRoot
#MP4文件录制大小,默认一个小时
fileSecond=3600
#每次流化MP4文件的时长,单位毫秒
sampleMS=100
[rtmp]
#握手超时时间默认15秒
handshakeSecond=15
#维持链接超时时间默认15秒
keepAliveSecond=15
#是否转换时间戳
modifyStamp=1
#rtmp端口号
port=1935
[rtp]
#RTP打包最大MTU,公网情况下更小
audioMtuSize=600
#如果RTP序列正确次数累计达到该数字就启动清空排序缓存
clearCount=10
#最大RTP时间为13个小时每13小时回环一次
cycleMS=46800000
#RTP排序缓存最大个数
maxRtpCount=50
#RTP打包最大MTU,公网情况下更小
videoMtuSize=1400
[rtsp]
#是否优先base64方式认证默认Md5方式认证
authBasic=0
#握手超时时间默认15秒
handshakeSecond=15
#维持链接超时时间默认15秒
keepAliveSecond=15
#rtsp端口号
port=554
sslport=322
[shell]
maxReqSize=1024
port=9000

View File

@ -0,0 +1,33 @@
################################################################################################################################
# 容器服务端口说明
# 10800: web管理界面
# 1935 : rtmp端口
# 554 : rtsp端口
# 80 : zlmedia服务端口
#
# 容器运行以后, 访问 http://[部署机ip]:[web管理端口|10800] 可以通过web管理,默认密码为123456 (conf.ini的secret)
# 如: http://127.0.0.1:10800
#
################################################################################################################################
version: "3"
services:
zlmedia:
image: cl-zlmedia:latest
container_name: zlmedia
restart: always
ports:
- 11935:1935
- 10800:10800
- 9099:80
- 1554:554
environment:
- REACT_APP_API_HOST=127.0.0.1:9099
volumes:
- /usr/share/zoneinfo/Asia/Shanghai:/usr/share/zoneinfo/Asia/Shanghai:ro
- /__your_store_dir__/zlmedia/log:/usr/src/ZLMediaKit/zldata/log
- /__your_store_dir__/zlmedia/httpRoot:/usr/src/ZLMediaKit/zldata/httpRoot
- /__your_store_dir__/zlmedia/dbdata:/usr/src/ZLMediaKit/zldata/dbdata
#- /__your_store_dir__/zlmedia/config.ini:/usr/src/ZLMediaKit/zldata/config.ini

View File

@ -0,0 +1,7 @@
#!/bin/bash
echo "env.sh start"
/usr/local/bin/zlmedia-ui-env.sh
echo "nginx starting"
/usr/sbin/service nginx start
echo "ZLMediaKit starting"
/usr/src/ZLMediaKit/build/bin/MediaServer --daemon --level 2

17
docker/nginx_zlmedia.conf Normal file
View File

@ -0,0 +1,17 @@
server {
listen 10800;
server_name localhost;
root /usr/src/ZLMediaKitUI/ui;
location / {
try_files $uri $uri/ /index.html;
}
location = /index.html {
expires -1;
}
location = /env-config.js {
expires -1;
}
}

12
docker/zlmedia-ui-env.sh Normal file
View File

@ -0,0 +1,12 @@
#!/bin/bash
# Recreate config file
rm -rf /usr/src/ZLMediaKitUI/ui/env-config.js
touch /usr/src/ZLMediaKitUI/ui/env-config.js
# Add assignment
echo "window._env_ = {" >> /usr/src/ZLMediaKitUI/ui/env-config.js
echo " REACT_APP_API_HOST: \"${REACT_APP_API_HOST}\"" >> /usr/src/ZLMediaKitUI/ui/env-config.js
echo "}" >> /usr/src/ZLMediaKitUI/ui/env-config.js

View File

@ -1,7 +1,4 @@
include_directories(../3rdpart)
file(GLOB jsoncpp_src_list ../3rdpart/jsoncpp/*.cpp ../3rdpart/jsoncpp/*.h )
add_library(jsoncpp STATIC ${jsoncpp_src_list})
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
set(MediaServer_src_list ./WebApi.cpp ./WebHook.cpp main.cpp)
@ -10,13 +7,13 @@ else()
endif()
message(STATUS ${MediaServer_src_list})
#add_compile_options(-l sqlite3)
add_executable(MediaServer ${MediaServer_src_list})
if(WIN32)
set_target_properties(MediaServer PROPERTIES COMPILE_FLAGS ${VS_FALGS} )
endif()
target_link_libraries(MediaServer jsoncpp ${LINK_LIB_LIST})
target_link_libraries(MediaServer ${LINK_LIB_LIST} dl)

View File

@ -29,6 +29,8 @@
#include "Common/MediaSource.h"
#include "Util/File.h"
#include "System.h"
#include "Kf/Globals.h"
#include "Kf/DbUtil.h"
namespace FFmpeg {
#define FFmpeg_FIELD "ffmpeg."
@ -38,7 +40,8 @@ const char kLog[] = FFmpeg_FIELD"log";
onceToken token([]() {
mINI::Instance()[kBin] = trim(System::execute("which ffmpeg"));
mINI::Instance()[kCmd] = "%s -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s";
//chenxiaolei config.ini的[ffmpeg]cmd 去掉其中的第一个%s , ffmpeg_bin
mINI::Instance()[kCmd] = "-i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s";
mINI::Instance()[kLog] = exeDir() + "ffmpeg/ffmpeg.log";
});
}
@ -52,18 +55,29 @@ FFmpegSource::~FFmpegSource() {
DebugL;
}
void FFmpegSource::play(const string &src_url,const string &dst_url,int timeout_ms,const onPlay &cb) {
//chenxiaolei 支持单独为每一次 play 单独配置 ffmpeg 参数
void FFmpegSource::play(const string &src_url,const string &dst_url,int timeout_ms,const string &ffmpegCmd,const onPlay &cb) {
GET_CONFIG(string,ffmpeg_bin,FFmpeg::kBin);
GET_CONFIG(string,ffmpeg_cmd,FFmpeg::kCmd);
GET_CONFIG(string,ffmpeg_log,FFmpeg::kLog);
//chenxiaolei 支持单独为每一次 play 单独配置 ffmpeg 参数
_ffmpegCmd = ffmpegCmd;
_src_url = src_url;
_dst_url = dst_url;
_media_info.parse(dst_url);
//chenxiaolei config.ini的[ffmpeg]cmd 去掉其中的第一个%s , ffmpeg_bin
MediaInfo _src_media_info;
_src_media_info.parse(src_url);
string tempFFmpegCmd= _ffmpegCmd.empty() ? ffmpeg_cmd : _ffmpegCmd;
tempFFmpegCmd= ffmpeg_bin + " " + tempFFmpegCmd;
char cmd[1024] = {0};
snprintf(cmd, sizeof(cmd),ffmpeg_cmd.data(),ffmpeg_bin.data(),src_url.data(),dst_url.data());
snprintf(cmd, sizeof(cmd),tempFFmpegCmd.data(),src_url.data(),dst_url.data());
_process.run(cmd,ffmpeg_log);
InfoL << cmd;
@ -192,7 +206,7 @@ void FFmpegSource::startTimer(int timeout_ms) {
//同步查找流
if (!src) {
//流不在线,重新拉流
strongSelf->play(strongSelf->_src_url, strongSelf->_dst_url, timeout_ms,
strongSelf->play(strongSelf->_src_url, strongSelf->_dst_url, timeout_ms, strongSelf ->_ffmpegCmd ,
[](const SockException &) {});
}
});
@ -200,7 +214,7 @@ void FFmpegSource::startTimer(int timeout_ms) {
//推流给其他服务器的我们通过判断FFmpeg进程是否在线如果FFmpeg推流中断那么它应该会自动退出
if (!strongSelf->_process.wait(false)) {
//ffmpeg不在线重新拉流
strongSelf->play(strongSelf->_src_url, strongSelf->_dst_url, timeout_ms, [](const SockException &) {});
strongSelf->play(strongSelf->_src_url, strongSelf->_dst_url, timeout_ms, strongSelf ->_ffmpegCmd, [](const SockException &) {});
}
}
return true;
@ -223,6 +237,17 @@ void FFmpegSource::startTimer(int timeout_ms) {
//该流无人观看,我们停止吧
if(strongSelf->_onClose){
//InfoL << "用户停止播放,频道无人观看:" << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId();
//chenxiaolei 根据数据库中的配置(是否录像)来决定是否停止
Json::Value tProxyData = searchChannel(sender.getVhost(), sender.getApp(),sender.getId());
if(!tProxyData.isNull()) {
int vRecordMp4 = tProxyData["record_mp4"].asInt();
if(vRecordMp4){
//InfoL << "频道保持录像,忽略停止拉流:" << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId();
return;
}
}
strongSelf->_onClose();
}
});

View File

@ -51,7 +51,7 @@ public:
* @param cb
*/
void setOnClose(const function<void()> &cb);
void play(const string &src_url,const string &dst_url,int timeout_ms,const onPlay &cb);
void play(const string &src_url,const string &dst_url,int timeout_ms,const string &ffmpegCmd, const onPlay &cb);
private:
void findAsync(int maxWaitMS ,const function<void(const MediaSource::Ptr &src)> &cb);
void startTimer(int timeout_ms);
@ -60,6 +60,7 @@ private:
Timer::Ptr _timer;
EventPoller::Ptr _poller;
MediaInfo _media_info;
string _ffmpegCmd;
string _src_url;
string _dst_url;
function<void()> _onClose;

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,7 @@
#define ZLMEDIAKIT_WEBAPI_H
#include <string>
#include "FFmpegSource.h"
using namespace std;
namespace mediakit {
@ -44,8 +45,21 @@ extern const string kPort;
} // namespace mediakit
//chenxiaolei 新增数据库配置的通道使用的proxyMap
extern unordered_map<string ,PlayerProxy::Ptr> m_s_proxyMap;
extern recursive_mutex m_s_proxyMapMtx;
//chenxiaolei 新增数据库配置的通道使用的proxyMap
extern unordered_map<string ,FFmpegSource::Ptr> m_s_ffmpegMap;
extern recursive_mutex m_s_ffmpegMapMtx;
//chenxiaolei 配置生效方法
extern void processProxyCfg(const Json::Value &proxyData, const bool initialize);
//chenxiaolei 配置(数组,多个)生效方法
extern void processProxyCfgs(const Json::Value &cfg_root);
extern void installWebApi();
extern void unInstallWebApi();
void installWebApi();
void unInstallWebApi();
#endif //ZLMEDIAKIT_WEBAPI_H

View File

@ -26,12 +26,16 @@
#include <map>
#include <signal.h>
#include <jsoncpp/value.h>
#include <jsoncpp/json.h>
#include <iostream>
#include "Util/MD5.h"
#include "Util/File.h"
#include "Util/logger.h"
#include "Util/SSLBox.h"
#include "Util/onceToken.h"
#include "Kf/DbUtil.h"
#include "Kf/Globals.h"
#include "Util/CMD.h"
#include "Network/TcpServer.h"
#include "Poller/EventPoller.h"
@ -46,6 +50,7 @@
#include "WebApi.h"
#include "WebHook.h"
#if !defined(_WIN32)
#include "System.h"
#endif//!defined(_WIN32)
@ -197,6 +202,58 @@ static void inline listen_shell_input(){
}
#endif//!defined(_WIN32)
//chenxiaolei 适配数据库中的配置数据
void initEventListener() {
static onceToken s_token([]() {
//当频道没有人观看时触发
//流无人观看并且超过若干时间后才触发kBroadcastStreamNoneReader事件
//默认连续streamNoneReaderDelayMS无人观看然后触发
NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastStreamNoneReader,[](BroadcastStreamNoneReaderArgs) {
/**
*
*/
InfoL << "用户停止播放,频道无人观看:" << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId();
Json::Value tProxyData = searchChannel(sender.getVhost(), sender.getApp(),sender.getId());
if(!tProxyData.isNull()) {
int vRecordMp4 = tProxyData.get("record_mp4",0).asInt();
bool vOnDemand = tProxyData.get("on_demand",true).asBool();
bool realOnDemand = vRecordMp4 ? false : vOnDemand;
if(!realOnDemand){
InfoL << "频道保持录像,忽略停止拉流:" << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId();
return;
}
}
InfoL << "频道临时关闭,开始停止拉流:" << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId();
sender.close(true);
});
//监听播放失败(未找到特定的流)事件, 之前没人看,突然有人看的时候
//等待流注册超时时间,收到播放器后请求后,如果未找到相关流,服务器会等待一定时间,
//如果在这个时间内,相关流注册上了,那么服务器会立即响应播放器播放成功,
//否则会最多等待kMaxStreamWaitTimeMS毫秒然后响应播放器播放失败
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastNotFoundStream,[](BroadcastNotFoundStreamArgs){
/**
*
* ZLMediaKit会把其立即转发给播放器(maxStreamWaitMSmaxStreamWaitMS都未拉流成功)
*/
InfoL << "频道上未找到流:" << args._schema << "/" << args._vhost << "/" << args._app << "/" << args._streamid << "/" << args._param_strs ;
Json::Value tProxyData = searchChannel(args._vhost,args._app,args._streamid);
if(!tProxyData.isNull() && tProxyData["active"].asInt()) {
InfoL << "为频道重新拉流:" << args._schema << "/" << args._vhost << "/" << args._app << "/" << args._streamid << "/" << args._param_strs << tProxyData["id"] ;
processProxyCfg(tProxyData, false);
}
});
}, nullptr);
}
int main(int argc,char *argv[]) {
{
CMD_main cmd_main;
@ -215,11 +272,15 @@ int main(int argc,char *argv[]) {
int threads = cmd_main["threads"];
//设置日志
//chenxiaolei 日志存储目录调整
string logDir =exeDir()+ "log/";
File::createfile_path(logDir.data(), S_IRWXO | S_IRWXG | S_IRWXU);
Logger::Instance().add(std::make_shared<ConsoleChannel>("ConsoleChannel", logLevel));
#if defined(__linux__) || defined(__linux)
Logger::Instance().add(std::make_shared<SysLogChannel>("SysLogChannel",logLevel));
#else
Logger::Instance().add(std::make_shared<FileChannel>("FileChannel", exePath() + ".log", logLevel));
Logger::Instance().add(std::make_shared<FileChannel>("FileChannel", logDir + exeName() + ".log", logLevel));
#endif
#if !defined(_WIN32)
@ -231,11 +292,35 @@ int main(int argc,char *argv[]) {
System::systemSetup();
#endif//!defined(_WIN32)
//初始化sqlite数据库
string dbDataDir =exeDir()+ "dbdata/";
File::createfile_path(dbDataDir.data(), S_IRWXO | S_IRWXG | S_IRWXU);
initDatabase(dbDataDir);
//启动异步日志线程
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
//加载配置文件,如果配置文件不存在就创建一个
loadIniConfig(ini_file.data());
uint16_t shellPort = mINI::Instance()[Shell::kPort];
uint16_t rtspPort = mINI::Instance()[Rtsp::kPort];
uint16_t rtspsPort = mINI::Instance()[Rtsp::kSSLPort];
uint16_t rtmpPort = mINI::Instance()[Rtmp::kPort];
uint16_t httpPort = mINI::Instance()[Http::kPort];
uint16_t httpsPort = mINI::Instance()[Http::kSSLPort];
//清理下无效临时录像
clearInvalidRecord(mINI::Instance()[Record::kFilePath]);
//执行转发规则
Json::Value cfg_root = searchChannels();
processProxyCfgs(cfg_root);
//事件监听
initEventListener();
//加载证书,证书包含公钥和私钥
SSL_Initor::Instance().loadCertificate(ssl_file.data());
//信任某个自签名证书
@ -243,12 +328,6 @@ int main(int argc,char *argv[]) {
//不忽略无效证书证书(例如自签名或过期证书)
SSL_Initor::Instance().ignoreInvalidCertificate(true);
uint16_t shellPort = mINI::Instance()[Shell::kPort];
uint16_t rtspPort = mINI::Instance()[Rtsp::kPort];
uint16_t rtspsPort = mINI::Instance()[Rtsp::kSSLPort];
uint16_t rtmpPort = mINI::Instance()[Rtmp::kPort];
uint16_t httpPort = mINI::Instance()[Http::kPort];
uint16_t httpsPort = mINI::Instance()[Http::kSSLPort];
//设置poller线程数,该函数必须在使用ZLToolKit网络相关对象之前调用才能生效
EventPollerPool::setPoolSize(threads);

View File

@ -42,8 +42,9 @@ DevChannel::DevChannel(const string &strVhost,
const string &strId,
float fDuration,
bool bEanbleHls,
bool bEnableMp4) :
MultiMediaSourceMuxer(strVhost, strApp, strId, fDuration, bEanbleHls, bEnableMp4) {}
//chenxiaolei 修改为int, 录像最大录制天数,0就是不录
int bRecordMp4) :
MultiMediaSourceMuxer(strVhost, strApp, strId, fDuration, bEanbleHls, bRecordMp4) {}
DevChannel::~DevChannel() {}

View File

@ -75,7 +75,8 @@ public:
const string &strId,
float fDuration = 0,
bool bEanbleHls = true,
bool bEnableMp4 = false);
//chenxiaolei 修改为int, 录像最大录制天数,0就是不录
int bRecordMp4 = 0);
virtual ~DevChannel();

View File

@ -40,10 +40,11 @@ public:
const string &strId,
float dur_sec = 0.0,
bool bEanbleHls = true,
bool bEnableMp4 = false){
//chenxiaolei 修改为int, 录像最大录制天数,0就是不录
int bRecordMp4 = false){
_rtmp = std::make_shared<RtmpMediaSourceMuxer>(vhost,strApp,strId,std::make_shared<TitleMete>(dur_sec));
_rtsp = std::make_shared<RtspMediaSourceMuxer>(vhost,strApp,strId,std::make_shared<TitleSdp>(dur_sec));
_record = std::make_shared<MediaRecorder>(vhost,strApp,strId,bEanbleHls,bEnableMp4);
_record = std::make_shared<MediaRecorder>(vhost,strApp,strId,bEanbleHls,bRecordMp4);
}
virtual ~MultiMediaSourceMuxer(){}

View File

@ -162,7 +162,8 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
if (rtppack->sequence != (uint16_t)(_h264frame->sequence + 1)) {
_h264frame->buffer.clear();
WarnL << "丢包,帧废弃:" << rtppack->sequence << "," << _h264frame->sequence;
//chenxiaolei 这个日志有些源,打印的太多,目测也不影响观看,调整为debug
DebugL << "丢包,帧废弃:" << rtppack->sequence << "," << _h264frame->sequence;
return false;
}
_h264frame->sequence = rtppack->sequence;

View File

@ -113,7 +113,8 @@ bool H265RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
if (rtppack->sequence != (uint16_t) (_h265frame->sequence + 1)) {
_h265frame->buffer.clear();
WarnL << "丢包,帧废弃:" << rtppack->sequence << "," << _h265frame->sequence;
//chenxiaolei 这个日志有些源,打印的太多,目测也不影响观看,调整为debug
DebugL << "丢包,帧废弃:" << rtppack->sequence << "," << _h265frame->sequence;
return false;
}
_h265frame->sequence = rtppack->sequence;

View File

@ -122,6 +122,8 @@ int64_t HttpSession::onRecvHeader(const char *header,uint64_t len) {
static onceToken token([]() {
g_mapCmdIndex.emplace("GET",&HttpSession::Handle_Req_GET);
g_mapCmdIndex.emplace("POST",&HttpSession::Handle_Req_POST);
//chenxiaolei 增加OPTIONS,以便支持web 页面的跨域嗅探请求
g_mapCmdIndex.emplace("OPTIONS",&HttpSession::Handle_Req_OPTIONS);
}, nullptr);
_parser.Parse(header);
@ -762,6 +764,12 @@ inline HttpSession::KeyValue HttpSession::makeHttpHeader(bool bClose, int64_t iC
GET_CONFIG(uint32_t,keepAliveSec,Http::kKeepAliveSecond);
GET_CONFIG(uint32_t,reqCnt,Http::kMaxReqCount);
//chenxiaolei 请求跨域支持
headerOut.emplace("Access-Control-Allow-Headers", "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization");
headerOut.emplace("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS, DELETE");
headerOut.emplace("Access-Control-Allow-Origin", "*");
headerOut.emplace("Access-Control-Expose-Headers", "Location");
headerOut.emplace("Date", dateStr());
headerOut.emplace("Server", SERVER_NAME);
headerOut.emplace("Connection", bClose ? "close" : "keep-alive");
@ -834,6 +842,13 @@ inline bool HttpSession::emitHttpEvent(bool doInvoke){
}
return consumed;
}
//chenxiaolei 增加OPTIONS,以便支持web 页面的跨域嗅探请求
inline void HttpSession::Handle_Req_OPTIONS(int64_t &content_len) {
sendResponse( "200 OK" , makeHttpHeader(false, 0), "");
shutdown(SockException(Err_shutdown,"recv http content completed"));
}
inline void HttpSession::Handle_Req_POST(int64_t &content_len) {
GET_CONFIG(uint64_t,maxReqSize,Http::kMaxReqSize);
GET_CONFIG(int,maxReqCnt,Http::kMaxReqCount);

View File

@ -103,6 +103,8 @@ protected:
}
private:
//chenxiaolei 增加OPTIONS,以便支持web 页面的跨域嗅探请求
inline void Handle_Req_OPTIONS(int64_t &content_len);
inline void Handle_Req_GET(int64_t &content_len);
inline void Handle_Req_POST(int64_t &content_len);
inline bool checkLiveFlvStream();

532
src/Kf/DbUtil.cpp Normal file
View File

@ -0,0 +1,532 @@
//
// Created by 陈磊 on 2019-06-24.
//
#include <string>
#include "DbUtil.h"
#include "Globals.h"
#include <functional>
#include <sqlite3pp/sqlite3pp.h>
#include "jsoncpp/json.h"
#include "Common/config.h"
#include <unordered_map>
static ChannelPropTupleList ctl;
static std::unordered_map<string, ChannelPropTuple> ctm;
void initChannelAss() {
ctl.push_back(ChannelPropTuple("id", "ID", "int"));
ctl.push_back(ChannelPropTuple("name", "通道名称", "string"));
ctl.push_back(ChannelPropTuple("vhost", "虚拟主机Vhost", "string"));
ctl.push_back(ChannelPropTuple("app", "应用标识App", "string"));
ctl.push_back(ChannelPropTuple("stream", "通道标识Stream", "string"));
ctl.push_back(ChannelPropTuple("source_url", "接入地址", "string"));
ctl.push_back(ChannelPropTuple("ffmpeg_cmd", "FFMpeg拉流参数", "string"));
ctl.push_back(ChannelPropTuple("enable_hls", "是否开启HLS", "int"));
ctl.push_back(ChannelPropTuple("record_mp4", "录像保留(天)", "int"));
ctl.push_back(ChannelPropTuple("rtsp_transport", "RTSP协议(1:TCP,2:UDP)", "int"));
ctl.push_back(ChannelPropTuple("on_demand", "按需直播", "int"));
ctl.push_back(ChannelPropTuple("active", "是否启用", "int"));
for (const auto &item : ctl) {
ChannelPropTuple c = item;
string desc = std::get<1>(c);
ctm[desc] = c;
}
}
std::unordered_map<string, ChannelPropTuple> getChannelPropsMap() {
return ctm;
}
ChannelPropTupleList getChannelPropTupleList() {
return ctl;
}
std::string channelsJsonToCsvStr(Json::Value channels) {
auto channelsData = channels.isNull() ? Json::Value(Json::ValueType::arrayValue) : channels;
_StrPrinter printer;
printer << "\xef\xbb\xbf"; // BOM UTF-8
ChannelPropTupleList props = ctl;
for (auto &pp : props) {
printer << std::get<1>(pp) << ",";
}
printer << "\r\n";
for (Json::Value::ArrayIndex i = 0; i != channelsData.size(); i++) {
Json::Value cConfig = channelsData[i];
for (auto &pp : props) {
printer << cConfig[std::get<0>(pp)] << ",";
}
printer << "\r\n" << endl;
}
return printer;
}
Json::Value channelsCsvStrToJson(std::string channelsCsv) {
Json::Value csvJsonRet;
vector<string> rows = split(channelsCsv, "\r\n");
if (rows.size() == 0) {
throw std::invalid_argument("无效的csv文件,请检查!");
} else {
if (rows.size() > 1) {
string titleRowStr = rows[0];
vector<string> titleCols = split(titleRowStr, ",");
vector<ChannelPropTuple> titlePropCols;
for (vector<string>::iterator iter = titleCols.begin(); iter != titleCols.end(); iter++) {
string propName = trim(*iter, "\n\r \xef\xbb\xbf");
ChannelPropTuple prop = ctm[propName];
titlePropCols.push_back(prop);
}
for (int i = 1; i < rows.size(); i++) {
auto dataRow = rows[i];
vector<string> dataCols = split2(dataRow, ",");
Json::Value rowJson;
for (int m = 0; m < dataCols.size(); m++) {
ChannelPropTuple prop = titlePropCols[m];
string propName = std::get<0>(prop);
string propType = std::get<2>(prop);
string val = trim(dataCols[m], "\"");
if (!propName.empty()) {
if (propType.compare("int") == 0) {
rowJson[propName] = atoi(val.c_str());
} else {
rowJson[propName] = val;
}
}
}
csvJsonRet.append(rowJson);
}
}
}
InfoL << "csvJsonRet" << ": " << csvJsonRet.toStyledString();
return csvJsonRet;
}
static string dbpath = "zlmedia.db";
void initDatabase(string dbpathParent) {
initChannelAss();
dbpath = dbpathParent+dbpath;
InfoL << "dbpath: " << dbpath;
sqlite3pp::database db(dbpath.data());
try {
string channelTableCreateSql = "CREATE TABLE IF NOT EXISTS CHANNEL(" \
"ID INTEGER PRIMARY KEY AUTOINCREMENT," \
"PROXY_KEY TEXT NOT NULL UNIQUE," \
"NAME TEXT NOT NULL," \
"VHOST TEXT NOT NULL," \
"APP TEXT NOT NULL," \
"STREAM TEXT NOT NULL," \
"SOURCE_URL TEXT NOT NULL," \
"FFMPEG_CMD TEXT NOT NULL," \
"ENABLE_HLS TINYINT NOT NULL," \
"RECORD_MP4 INT NOT NULL," \
"RTSP_TRANSPORT TINYINT NOT NULL," \
"ON_DEMAND TINYINT NOT NULL," \
"ACTIVE TINYINT NOT NULL," \
"CREATE_TIME DATETIME NOT NULL," \
"MODIFY_TIME DATETIME DEFAULT (datetime('now', 'localtime'))" \
");";
db.execute(channelTableCreateSql.data());
} catch (exception &ex) {
ErrorL << ex.what();
}
}
int deleteChannel(int channelId, std::function<void()> cb) {
try {
sqlite3pp::database db(dbpath.data());
sqlite3pp::transaction xct(db, true);
sqlite3pp::command cmd(db, " DELETE FROM CHANNEL " \
"WHERE " \
"ID = :id");
cmd.bind(":id", channelId);
int rc = cmd.execute();
xct.commit();
if (rc == SQLITE_OK) {
//回调
cb();
}
return rc;
} catch (exception &ex) {
ErrorL << "删除 channel通道 失败" << ex.what();
}
return -1;
}
int updateChannel(int channelId, Json::Value jsonArgs, std::function<void(Json::Value channel)> cb) {
try {
sqlite3pp::database db(dbpath.data());
sqlite3pp::transaction xct(db, true);
sqlite3pp::command cmd(db, "UPDATE CHANNEL " \
"SET " \
"PROXY_KEY=:proxyKey, " \
"NAME=:name, " \
"VHOST=:vhost, " \
"APP=:app, "\
"STREAM=:stream, "\
"SOURCE_URL=:source_url, " \
"FFMPEG_CMD=:ffmpeg_cmd, " \
"ENABLE_HLS=:enable_hls, " \
"RECORD_MP4=:record_mp4, " \
"RTSP_TRANSPORT=:rtsp_transport, " \
"ON_DEMAND=:on_demand, " \
"ACTIVE=:active," \
"MODIFY_TIME=datetime('now', 'localtime') " \
"WHERE " \
"ID = :id");
string vhost = jsonArgs.get("vhost", DEFAULT_VHOST).asString();
string app = jsonArgs["app"].asString();
string stream = jsonArgs["stream"].asString();
string proxyKey = getProxyKey(vhost, app, stream);
cmd.bind(":proxyKey", proxyKey, sqlite3pp::copy);
cmd.bind(":name", jsonArgs["name"].asString(), sqlite3pp::copy);
cmd.bind(":vhost", vhost, sqlite3pp::copy);
cmd.bind(":app", app, sqlite3pp::copy);
cmd.bind(":stream", stream, sqlite3pp::copy);
cmd.bind(":source_url", jsonArgs["source_url"].asString(), sqlite3pp::copy);
cmd.bind(":ffmpeg_cmd", jsonArgs.get("ffmpeg_cmd", "").asString(), sqlite3pp::copy);
cmd.bind(":enable_hls", jsonArgs.get("enable_hls", 1).asInt());
cmd.bind(":record_mp4", jsonArgs.get("record_mp4", 0).asInt());
cmd.bind(":rtsp_transport", jsonArgs.get("rtsp_transport", 1).asInt());
cmd.bind(":on_demand", jsonArgs.get("on_demand", 1).asInt());
cmd.bind(":active", jsonArgs.get("active", 0).asInt());
cmd.bind(":id", channelId);
int rc = cmd.execute();
xct.commit();
if (rc == SQLITE_OK) {
Json::Value ret = jsonArgs;
ret["proxyKey"] = proxyKey;
//回调
cb(ret);
}
return rc;
} catch (exception &ex) {
ErrorL << "更新 channel通道 失败" << ex.what();
}
return -1;
}
extern int saveChannel(int channelId, Json::Value jsonArgs,
std::function<void(bool isCreate, Json::Value originalChannel, Json::Value channel)> cb) {
int rc = 0;
auto createFunc = [jsonArgs, cb]() {
return createChannel(jsonArgs, [cb](Json::Value channel) {
cb(true, channel, channel);
});
};
if (channelId == 0) {
rc = createFunc();
} else {
Json::Value originalChannel = searchChannel(channelId);
if (originalChannel.isNull()) {
rc = createFunc();
} else {
rc = updateChannel(channelId, jsonArgs, [cb, originalChannel](Json::Value channel) {
cb(false, originalChannel, channel);
});
}
}
return rc;
}
int createChannel(Json::Value jsonArgs, std::function<void(Json::Value channel)> cb) {
try {
sqlite3pp::database db(dbpath.data());
sqlite3pp::transaction xct(db, true);
sqlite3pp::command cmd(db, "INSERT INTO CHANNEL " \
" (ID, PROXY_KEY, NAME, VHOST, APP, STREAM, SOURCE_URL, FFMPEG_CMD, ENABLE_HLS, RECORD_MP4, RTSP_TRANSPORT, ON_DEMAND, ACTIVE, CREATE_TIME)" \
" VALUES" \
" (:id, :proxyKey, :name, :vhost, :app, :stream, :source_url, :ffmpeg_cmd, :enable_hls, :record_mp4, :rtsp_transport, :on_demand, :active, datetime('now', 'localtime'))");
string vhost = jsonArgs.get("vhost", DEFAULT_VHOST).asString();
string app = jsonArgs["app"].asString();
string stream = jsonArgs["stream"].asString();
string proxyKey = getProxyKey(vhost, app, stream);
cmd.bind(":proxyKey", proxyKey, sqlite3pp::copy);
if (jsonArgs["id"].isNull()) {
cmd.bind(":id", sqlite3pp::null_type());
} else {
cmd.bind(":id", jsonArgs["id"].asInt());
}
cmd.bind(":name", jsonArgs["name"].asString(), sqlite3pp::copy);
cmd.bind(":vhost", vhost, sqlite3pp::copy);
cmd.bind(":app", app, sqlite3pp::copy);
cmd.bind(":stream", stream, sqlite3pp::copy);
cmd.bind(":source_url", jsonArgs["source_url"].asString(), sqlite3pp::copy);
cmd.bind(":ffmpeg_cmd", jsonArgs.get("ffmpeg_cmd", "").asString(), sqlite3pp::copy);
cmd.bind(":enable_hls", jsonArgs.get("enable_hls", 1).asInt());
cmd.bind(":record_mp4", jsonArgs.get("record_mp4", 0).asInt());
cmd.bind(":rtsp_transport", jsonArgs.get("rtsp_transport", 1).asInt());
cmd.bind(":on_demand", jsonArgs.get("on_demand", 1).asInt());
cmd.bind(":active", jsonArgs.get("active", 0).asInt());
int rc = cmd.execute();
xct.commit();
if (rc == SQLITE_OK) {
sqlite3pp::query qry(db, "select last_insert_rowid()");
sqlite3pp::query::iterator lastRowIdIter = qry.begin();
int lastInsertId;
std::tie(lastInsertId) = (*lastRowIdIter).get_columns<int>(0);
Json::Value ret = jsonArgs;
ret["proxyKey"] = proxyKey;
ret["id"] = lastInsertId;
cb(ret);
}
return rc;
} catch (exception &ex) {
ErrorL << "创建 channel通道 失败" << ex.what();
}
return -1;
}
Json::Value searchChannels() {
return searchChannels("", "", "", 1, 99999);
}
int countChannels(string searchText, string enableMp4, string active) {
sqlite3pp::database db(dbpath.data());
string baseQuery = "SELECT count(*) FROM CHANNEL ";
string where = " where 1=1 ";
string conditions = "";
string query = baseQuery;
if (!enableMp4.empty() && atoi(enableMp4.c_str())) {
conditions += " and record_mp4 > 0 ";
}
if (!active.empty()) {
conditions += " and active=:active ";
}
if (!searchText.empty()) {
conditions += " and name like :searchText ";
}
if (!conditions.empty()) {
query += where + conditions;
}
sqlite3pp::query qry(db, query.c_str());
if (!searchText.empty()) {
qry.bind(":searchText", "%" + searchText + "%", sqlite3pp::copy);
}
if (!active.empty()) {
qry.bind(":active", atoi(active.c_str()));
}
sqlite3pp::query::iterator lastRowIdIter = qry.begin();
int total;
std::tie(total) = (*lastRowIdIter).get_columns<int>(0);
return total;
}
Json::Value searchChannels(string searchText, string enableMp4, string active, int page, int pageSize) {
sqlite3pp::database db(dbpath.data());
string baseQuery = "SELECT ID, PROXY_KEY, NAME, VHOST, APP, STREAM, SOURCE_URL, FFMPEG_CMD, ENABLE_HLS, RECORD_MP4, RTSP_TRANSPORT, ON_DEMAND, ACTIVE FROM CHANNEL ";
string where = " where 1=1 ";
string conditions = "";
string limit = " limit :pageSize offset :pageSize*(:page-1)";
string order = " order by ID ";
string query = baseQuery;
if (!enableMp4.empty() && atoi(enableMp4.c_str())) {
conditions += " and record_mp4 > 0 ";
}
if (!active.empty()) {
conditions += " and active=:active ";
}
if (!searchText.empty()) {
conditions += " and name like :searchText ";
}
if (!conditions.empty()) {
query += where + conditions;
}
query += order;
if (!(page == 1 && pageSize == 99999)) {
query = query + limit;
}
sqlite3pp::query qry(db, query.c_str());
if (!searchText.empty()) {
qry.bind(":searchText", "%" + searchText + "%", sqlite3pp::copy);
}
if (!active.empty()) {
qry.bind(":active", atoi(active.c_str()));
}
if (!(page == 1 && pageSize == 99999)) {
qry.bind(":pageSize", pageSize);
qry.bind(":page", page);
}
Json::Value ret;
for (sqlite3pp::query::iterator i = qry.begin(); i != qry.end(); ++i) {
int id, enable_hls, record_mp4, rtsp_transport, on_demand, active;
std::string proxyKey, name, vhost, app, stream, source_url, ffmpeg_cmd;
std::tie(id, proxyKey, name, vhost, app, stream, source_url, ffmpeg_cmd, enable_hls, record_mp4, rtsp_transport,
on_demand, active) =
(*i).get_columns <
int, char const*, char const*, char const*, char const*, char const*, char const*, char const*, int, int, int, int,
int > (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
//(*i).getter() >> sqlite3pp::ignore >> id >> name >> vhost >> app >> stream >> source_url >> ffmpeg_cmd >>enable_hls >> record_mp4 >> rtsp_transport >> on_demand >> active;
Json::Value rowRet;
rowRet["id"] = id;
rowRet["proxyKey"] = proxyKey;
rowRet["vhost"] = vhost;
rowRet["name"] = name;
rowRet["app"] = app;
rowRet["stream"] = stream;
rowRet["source_url"] = source_url;
rowRet["ffmpeg_cmd"] = ffmpeg_cmd;
rowRet["enable_hls"] = enable_hls;
rowRet["record_mp4"] = record_mp4;
rowRet["rtsp_transport"] = rtsp_transport;
rowRet["on_demand"] = on_demand;
rowRet["active"] = active;
ret.append(rowRet);
}
return ret;
}
Json::Value searchChannel(std::string vhost, std::string app, std::string stream) {
return searchChannel(getProxyKey(vhost, app, stream));
}
Json::Value searchChannel(std::string proxyKey) {
sqlite3pp::database db(dbpath.data());
std::string query =
"SELECT ID, PROXY_KEY, NAME, VHOST, APP, STREAM, SOURCE_URL, FFMPEG_CMD, ENABLE_HLS, RECORD_MP4, RTSP_TRANSPORT, ON_DEMAND, ACTIVE FROM CHANNEL WHERE PROXY_KEY=:proxyKey";
sqlite3pp::query qry(db, query.c_str());
qry.bind(":proxyKey", proxyKey, sqlite3pp::copy);
Json::Value rowRet;
for (sqlite3pp::query::iterator i = qry.begin(); i != qry.end(); ++i) {
int id, enable_hls, record_mp4, rtsp_transport, on_demand, active;
std::string proxyKey, name, vhost, app, stream, source_url, ffmpeg_cmd;
std::tie(id, proxyKey, name, vhost, app, stream, source_url, ffmpeg_cmd, enable_hls, record_mp4, rtsp_transport,
on_demand, active) =
(*i).get_columns <
int, char const*, char const*, char const*, char const*, char const*, char const*, char const*, int, int, int, int,
int > (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
//(*i).getter() >> sqlite3pp::ignore >> id >> name >> vhost >> app >> stream >> source_url >> ffmpeg_cmd >>enable_hls >> record_mp4 >> rtsp_transport >> on_demand >> active;
rowRet["id"] = id;
rowRet["proxyKey"] = proxyKey;
rowRet["vhost"] = vhost;
rowRet["name"] = name;
rowRet["app"] = app;
rowRet["stream"] = stream;
rowRet["source_url"] = source_url;
rowRet["ffmpeg_cmd"] = ffmpeg_cmd;
rowRet["enable_hls"] = enable_hls;
rowRet["record_mp4"] = record_mp4;
rowRet["rtsp_transport"] = rtsp_transport;
rowRet["on_demand"] = on_demand;
rowRet["active"] = active;
break;
}
return rowRet;
}
Json::Value searchChannel(int channelId) {
sqlite3pp::database db(dbpath.data());
std::string query =
"SELECT ID, PROXY_KEY, NAME, VHOST, APP, STREAM, SOURCE_URL, FFMPEG_CMD, ENABLE_HLS, RECORD_MP4, RTSP_TRANSPORT, ON_DEMAND, ACTIVE FROM CHANNEL WHERE ID=:channelId";
sqlite3pp::query qry(db, query.c_str());
qry.bind(":channelId", channelId);
Json::Value rowRet;
for (sqlite3pp::query::iterator i = qry.begin(); i != qry.end(); ++i) {
int id, enable_hls, record_mp4, rtsp_transport, on_demand, active;
std::string proxyKey, name, vhost, app, stream, source_url, ffmpeg_cmd;
std::tie(id, proxyKey, name, vhost, app, stream, source_url, ffmpeg_cmd, enable_hls, record_mp4, rtsp_transport,
on_demand, active) =
(*i).get_columns <
int, char const*, char const*, char const*, char const*, char const*, char const*, char const*, int, int, int, int,
int > (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
//(*i).getter() >> sqlite3pp::ignore >> id >> name >> vhost >> app >> stream >> source_url >> ffmpeg_cmd >>enable_hls >> record_mp4 >> rtsp_transport >> on_demand >> active;
rowRet["id"] = id;
rowRet["proxyKey"] = proxyKey;
rowRet["vhost"] = vhost;
rowRet["name"] = name;
rowRet["app"] = app;
rowRet["stream"] = stream;
rowRet["source_url"] = source_url;
rowRet["ffmpeg_cmd"] = ffmpeg_cmd;
rowRet["enable_hls"] = enable_hls;
rowRet["record_mp4"] = record_mp4;
rowRet["rtsp_transport"] = rtsp_transport;
rowRet["on_demand"] = on_demand;
rowRet["active"] = active;
break;
}
return rowRet;
}

42
src/Kf/DbUtil.h Normal file
View File

@ -0,0 +1,42 @@
//
// Created by 陈磊 on 2019-06-24.
//
#ifndef KF_DBUTIL_H
#define KF_DBUTIL_H
#include <string>
#include "jsoncpp/json.h"
#include <functional>
#include <unordered_map>
typedef std::tuple<std::string,std::string,std::string> ChannelPropTuple;
typedef std::vector<ChannelPropTuple> ChannelPropTupleList;
ChannelPropTupleList getChannelPropTupleList();
std::unordered_map<std::string, ChannelPropTuple> getChannelPropsMap();
std::string channelsJsonToCsvStr(Json::Value channels);
Json::Value channelsCsvStrToJson(std::string channelsCsv);
extern void initDatabase(std::string dbpathParent);
extern int saveChannel(int channelId, Json::Value jsonArgs, std::function<void(bool isCreate,Json::Value originalChannel, Json::Value channel)> cb);
extern int createChannel(Json::Value jsonArgs, std::function<void(Json::Value channel)> cb);
extern int updateChannel(int channelId, Json::Value jsonArgs, std::function<void(Json::Value channel)> cb);
extern int deleteChannel(int channelId, std::function<void()> cb);
extern int countChannels(std::string searchText, std::string enableMp4,std::string active);
extern Json::Value searchChannels(std::string searchText,std::string enableMp4,std::string active, int page, int pageSize);
extern Json::Value searchChannels();
extern Json::Value searchChannel(std::string vhost,std::string app, std::string stream);
extern Json::Value searchChannel(std::string proxyKef);
extern Json::Value searchChannel(int channelId);
#endif //KF_DBUTIL_H

121
src/Kf/Globals.cpp Normal file
View File

@ -0,0 +1,121 @@
//
// Created by 陈磊 on 2019-06-24.
//
#include <string>
#include "Player/PlayerProxy.h"
#include "Globals.h"
#include <jsoncpp/value.h>
#include <jsoncpp/json.h>
#include "Util/File.h"
#include <vector>
#include <dirent.h>
#include <regex>
string getProxyKey(const string &vhost, const string &app, const string &stream) {
return vhost + "/" + app + "/" + stream;
}
using file_filter_type=std::function<bool(const char *, const char *, const char *)>;
/*
* ()filter过滤器
* filter返回true时将文件名全路径加入std::vector
* sub为true时为目录递归
*
*/
vector<string> forEachFile(const std::string &dir_name, file_filter_type filter, bool sub = false) {
std::vector<string> v;
auto dir = opendir(dir_name.data());
struct dirent *ent;
if (dir) {
while ((ent = readdir(dir)) != NULL) {
auto p = std::string(dir_name).append({'/'}).append(ent->d_name);
if (sub) {
if (0 == strcmp(ent->d_name, "..") || 0 == strcmp(ent->d_name, ".")) {
continue;
} else if (File::is_dir(p.c_str())) {
auto r = forEachFile(p, filter, sub);
v.insert(v.end(), r.begin(), r.end());
continue;
}
}
if (sub || !File::is_dir(p.c_str())) {
//如果是文件则调用过滤器filter
if (filter(dir_name.data(), ent->d_name, p.c_str())) {
v.emplace_back(p);
}
}
}
closedir(dir);
}
return v;
}
/**
*
* : .14-33-01.mp4 .
* @param recordFilePath
*/
void clearInvalidRecord(const string &recordFilePath) {
forEachFile(recordFilePath,
// filter函数lambda表达式
[&](const char *path, const char *name, const char *fullpath) {
//判断时临时录像文件
if (std::regex_match(name, std::regex("\\.\\d{2}-\\d{2}-\\d{2}\\.mp4"))) {
File::delete_file(fullpath);
InfoL << "清理无效的临时录像文件成功:" << fullpath;
}
//因为文件已经已经在lambda表达式中处理了
//不需要for_each_file返回文件列表所以这里返回false
return false;
}, true//递归子目录
);
}
int getNumberOfDays(int year, int month) {
//leap year condition, if month is 2
if (month == 2) {
if ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0))
return 29;
else
return 28;
}
//months which has 31 days
else if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8
|| month == 10 || month == 12)
return 31;
else
return 30;
}
int getNumberOfDays(const std::string &monthStr) {
string year = monthStr.substr(0, 4);
string month = monthStr.substr(4);
return getNumberOfDays(atoi(year.c_str()), atoi(month.c_str()));
}
/****
* chenxiaolei ZlToolKit Util/util.cpp split , ,, split2
* : ZlToolKit::split(",,,,,", ",").size() == 0
* : split2(",,,,,",",").size() == 5
*/
std::vector<std::string> split2(std::string stringToBeSplitted, std::string delimeter) {
std::vector<std::string> splittedString;
int startIndex = 0;
int endIndex = 0;
while( (endIndex = stringToBeSplitted.find(delimeter, startIndex)) < stringToBeSplitted.size() )
{
std::string val = stringToBeSplitted.substr(startIndex, endIndex - startIndex);
splittedString.push_back(val);
startIndex = endIndex + delimeter.size();
}
if(startIndex < stringToBeSplitted.size())
{
std::string val = stringToBeSplitted.substr(startIndex);
splittedString.push_back(val);
}
return splittedString;
}

30
src/Kf/Globals.h Normal file
View File

@ -0,0 +1,30 @@
//
// Created by 陈磊 on 2019-06-24.
//
#ifndef KF_GLOBALS_H
#define KF_GLOBALS_H
#include <string>
#include "Player/PlayerProxy.h"
#include <jsoncpp/value.h>
#include <jsoncpp/json.h>
#include <vector>
extern string getProxyKey(const string &vhost,const string &app,const string &stream);
//遍历文件夹
extern vector<string> forEachFile(const string &dir_name, function<bool(const char *, const char *,const char*)> filter, bool sub);
//清除无效的录像文件
extern void clearInvalidRecord(const string &recordFilePath);
//获取指定月份的天数
extern int getNumberOfDays(int year, int month);
//获取指定月份的天数(YYYYMM)
extern int getNumberOfDays(const std::string &monthStr);
std::vector<std::string> split2(std::string stringToBeSplitted, std::string delimeter);
#endif //KF_GLOBALS_H

View File

@ -39,7 +39,8 @@ MediaRecorder::MediaRecorder(const string &strVhost_tmp,
const string &strApp,
const string &strId,
bool enableHls,
bool enableMp4) {
//chenxiaolei 修改为int, 录像最大录制天数,0就是不录
int recordMp4) {
GET_CONFIG(string,hlsPath,Hls::kFilePath);
GET_CONFIG(uint32_t,hlsBufSize,Hls::kFileBufSize);
@ -70,14 +71,15 @@ MediaRecorder::MediaRecorder(const string &strVhost_tmp,
GET_CONFIG(string,recordPath,Record::kFilePath);
GET_CONFIG(string,recordAppName,Record::kAppName);
if(enableMp4){
//chenxiaolei 修改为int, 录像最大录制天数,0就是不录
if(recordMp4){
string mp4FilePath;
if(enableVhost){
mp4FilePath = recordPath + "/" + strVhost + "/" + recordAppName + "/" + strApp + "/" + strId + "/";
} else {
mp4FilePath = recordPath + "/" + recordAppName + "/" + strApp + "/" + strId + "/";
}
_mp4Maker.reset(new Mp4Maker(mp4FilePath,strVhost,strApp,strId));
_mp4Maker.reset(new Mp4Maker(mp4FilePath,strVhost,strApp,strId,recordMp4));
}
#endif //defined(ENABLE_MP4V2)
}

View File

@ -44,7 +44,8 @@ public:
const string &strApp,
const string &strId,
bool enableHls = true,
bool enableMp4 = false);
//chenxiaolei 修改为int, 录像最大录制天数,0就是不录
int recordMp4 = 0);
virtual ~MediaRecorder();
/**

View File

@ -37,7 +37,13 @@
#include "Extension/H264.h"
#include "Extension/AAC.h"
#include "Thread/WorkThreadPool.h"
#include <stdio.h>
#include <dirent.h>
#include <regex>
#include "jsoncpp/json.h"
#include <vector>
using namespace Json;
using namespace toolkit;
namespace mediakit {
@ -58,12 +64,50 @@ string timeStr(const char *fmt) {
return buffer;
}
string timeStr2(std::time_t rawtime, const char *fmt) {
std::tm tm_snapshot;
auto time = ::time(NULL);
#if defined(_WIN32)
localtime_s(&tm_snapshot, &time); // thread-safe
#else
localtime_r(&rawtime, &tm_snapshot); // POSIX
#endif
const size_t size = 1024;
char buffer[size];
auto success = std::strftime(buffer, size, fmt, &tm_snapshot);
if (0 == success)
return string(fmt);
return buffer;
}
time_t string2time(const std::string& timeStr){
struct tm stTm;
sscanf(timeStr.c_str(), "%4d%2d%2d",
&(stTm.tm_year),
&(stTm.tm_mon),
&(stTm.tm_mday));
stTm.tm_year -= 1900;
stTm.tm_mon--;
stTm.tm_isdst = -1;
stTm.tm_hour = 0;
stTm.tm_min = 0;
stTm.tm_sec = 0;
return mktime(&stTm);
}
Mp4Maker::Mp4Maker(const string& strPath,
const string &strVhost,
const string &strApp,
const string &strStreamId) {
const string &strStreamId,
//chenxiaolei 修改为int, 录像最大录制天数,0就是不录
const int &recordMp4) {
DebugL << strPath;
_strPath = strPath;
_recordMp4 = recordMp4;
/////record 业务逻辑//////
_info.strAppName = strApp;
@ -117,9 +161,11 @@ void Mp4Maker::inputAAC(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp
void Mp4Maker::inputH264_l(void *pData, uint32_t ui32Length, uint32_t ui32Duration) {
GET_CONFIG(uint32_t,recordSec,Record::kFileSecond);
auto iType = H264_TYPE(((uint8_t*)pData)[4]);
if(iType == H264Frame::NAL_IDR && (_hMp4 == MP4_INVALID_FILE_HANDLE || _ticker.elapsedTime() > recordSec * 1000)){
//chenxiaolei 确保录像,不会跨天
if(iType == H264Frame::NAL_IDR && (_hMp4 == MP4_INVALID_FILE_HANDLE || _ticker.elapsedTime() > recordSec * 1000 || timeStr("%H%M%S")=="000000")){
//在I帧率处新建MP4文件
//如果文件未创建或者文件超过10分钟则创建新文件
//如果文件未创建或者文件超过recordSec秒(且不跨天)则创建新文件
createFile();
}
if (_hVideo != MP4_INVALID_TRACK_ID) {
@ -130,9 +176,10 @@ void Mp4Maker::inputH264_l(void *pData, uint32_t ui32Length, uint32_t ui32Durati
void Mp4Maker::inputAAC_l(void *pData, uint32_t ui32Length, uint32_t ui32Duration) {
GET_CONFIG(uint32_t,recordSec,Record::kFileSecond);
if (!_haveVideo && (_hMp4 == MP4_INVALID_FILE_HANDLE || _ticker.elapsedTime() > recordSec * 1000)) {
//chenxiaolei 确保录像,不会跨天
if (!_haveVideo && (_hMp4 == MP4_INVALID_FILE_HANDLE || _ticker.elapsedTime() > recordSec * 1000 || timeStr("%H%M%S")=="000000")) {
//在I帧率处新建MP4文件
//如果文件未创建或者文件超过10分钟则创建新文件
//如果文件未创建或者文件超过recordSec秒(且不跨天)则创建新文件
createFile();
}
if (_hAudio != MP4_INVALID_TRACK_ID) {
@ -144,7 +191,8 @@ void Mp4Maker::inputAAC_l(void *pData, uint32_t ui32Length, uint32_t ui32Duratio
void Mp4Maker::createFile() {
closeFile();
auto strDate = timeStr("%Y-%m-%d");
//chenxiaolei 录像父文件夹格式调整
auto strDate = timeStr("%Y%m%d");
auto strTime = timeStr("%H-%M-%S");
auto strFileTmp = _strPath + strDate + "/." + strTime + ".mp4";
auto strFile = _strPath + strDate + "/" + strTime + ".mp4";
@ -217,18 +265,72 @@ void Mp4Maker::asyncClose() {
auto hMp4 = _hMp4;
auto strFileTmp = _strFileTmp;
auto strFile = _strFile;
auto info = _info;
WorkThreadPool::Instance().getExecutor()->async([hMp4,strFileTmp,strFile,info]() {
auto info = _info;
//chenxiaolei 支持删除过期录像
auto strPath = _strPath;
auto recordMp4 = _recordMp4;
WorkThreadPool::Instance().getExecutor()->async([hMp4,strFileTmp,strFile,strPath,recordMp4,info]() {
//获取文件录制时间放在MP4Close之前是为了忽略MP4Close执行时间
const_cast<Mp4Info&>(info).ui64TimeLen = ::time(NULL) - info.ui64StartedTime;
//MP4Close非常耗时所以要放在后台线程执行
MP4Close(hMp4,MP4_CLOSE_DO_NOT_COMPUTE_BITRATE);
//临时文件名改成正式文件名防止mp4未完成时被访问
rename(strFileTmp.data(),strFile.data());
//chenxiaolei 删除过期录像
if(recordMp4){
auto curTimeStr = timeStr("%Y%m%d");
DIR *dr = opendir(strPath.data());
if (dr != NULL) {
std::vector<string> delDirPaths;
struct dirent *de;
while ((de = readdir(dr)) != NULL){
if(!std::regex_match(de->d_name, std::regex("\\d{4}\\d{2}\\d{2}"))){
continue;
}
auto curTime=string2time(curTimeStr);
auto targetTime=string2time(de->d_name);
double dSec= std::difftime(curTime, targetTime);
double dDay= dSec/60/60/24;
if( dDay > recordMp4){
/*auto delDirPath= (strPath + (de->d_name)).data();
File::delete_file(delDirPath);*/
auto delDirPath= (strPath + (de->d_name));
delDirPaths.emplace_back(delDirPath);
//system(("rm -rf " + delDirPath+"").data());
//WarnL << "删除过期录像文件:"<< (strPath + (de->d_name)).data() ;
}
}
closedir(dr);
for (auto val : delDirPaths){
File::delete_file(val.data());
WarnL << "删除过期录像文件:"<< val;
}
}
}
//获取文件大小
struct stat fileData;
stat(strFile.data(), &fileData);
const_cast<Mp4Info&>(info).ui64FileSize = fileData.st_size;
//chenxiaolei 生成录像文件的信息文件(记录录像时长,开始时间,持续时长等)
Json::Value infoJson;
infoJson["startAt"] = timeStr2(info.ui64StartedTime,"%Y%m%d%H%M%S");
infoJson["duration"] = (int)(info.ui64TimeLen) ;
infoJson["mp4"] = info.strUrl;
auto strInfoFile = strFile +".json";
ofstream os;
os.open(strInfoFile);
Json::StyledWriter sw;
os << sw.write(infoJson);
os.close();
/////record 业务逻辑//////
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordMP4,info);
});

View File

@ -63,7 +63,9 @@ public:
Mp4Maker(const string &strPath,
const string &strVhost ,
const string &strApp,
const string &strStreamId);
const string &strStreamId,
//chenxiaolei 修改为int, 录像最大录制天数,0就是不录
const int &recordMp4);
virtual ~Mp4Maker();
private:
/**
@ -92,6 +94,8 @@ private:
MP4FileHandle _hMp4 = MP4_INVALID_FILE_HANDLE;
MP4TrackId _hVideo = MP4_INVALID_TRACK_ID;
MP4TrackId _hAudio = MP4_INVALID_TRACK_ID;
//chenxiaolei 修改为int, 录像最大录制天数,0就是不录
int _recordMp4;
string _strPath;
string _strFile;
string _strFileTmp;

View File

@ -66,14 +66,16 @@ PlayerProxy::PlayerProxy(const string &strVhost,
const string &strApp,
const string &strSrc,
bool bEnableHls,
bool bEnableMp4,
//chenxiaolei 修改为int, 录像最大录制天数,0就是不录
int bRecordMp4,
int iRetryCount,
const EventPoller::Ptr &poller) : MediaPlayer(poller){
_strVhost = strVhost;
_strApp = strApp;
_strSrc = strSrc;
_bEnableHls = bEnableHls;
_bEnableMp4 = bEnableMp4;
//chenxiaolei 修改为int, 录像最大录制天数,0就是不录
_bRecordMp4 = bRecordMp4;
_iRetryCount = iRetryCount;
}
@ -197,7 +199,8 @@ private:
};
void PlayerProxy::onPlaySuccess() {
_mediaMuxer.reset(new MultiMediaSourceMuxer(_strVhost,_strApp,_strSrc,getDuration(),_bEnableHls,_bEnableMp4));
//chenxiaolei 修改为int, 录像最大录制天数,0就是不录
_mediaMuxer.reset(new MultiMediaSourceMuxer(_strVhost,_strApp,_strSrc,getDuration(),_bEnableHls,_bRecordMp4));
_mediaMuxer->setListener(shared_from_this());
auto videoTrack = getTrack(TrackVideo,false);

View File

@ -50,7 +50,8 @@ public:
const string &strApp,
const string &strSrc,
bool bEnableHls = true,
bool bEnableMp4 = false,
//chenxiaolei 修改为int, 录像最大录制天数,0就是不录
int bRecordMp4 = 0,
int iRetryCount = -1,
const EventPoller::Ptr &poller = nullptr);
@ -86,7 +87,8 @@ private:
void onPlaySuccess();
private:
bool _bEnableHls;
bool _bEnableMp4;
//chenxiaolei 修改为int, 录像最大录制天数,0就是不录
int _bRecordMp4;
int _iRetryCount;
MultiMediaSourceMuxer::Ptr _mediaMuxer;
string _strVhost;

View File

@ -28,6 +28,10 @@
#include "RtmpSession.h"
#include "Common/config.h"
#include "Util/onceToken.h"
#include "Kf/Globals.h"
#include "Kf/DbUtil.h"
#include <jsoncpp/value.h>
#include <jsoncpp/json.h>
namespace mediakit {
@ -167,7 +171,17 @@ void RtmpSession::onCmd_publish(AMFDecoder &dec) {
shutdown(SockException(Err_shutdown,errMsg));
return;
}
_pPublisherSrc.reset(new RtmpToRtspMediaSource(_mediaInfo._vhost,_mediaInfo._app,_mediaInfo._streamid));
//chenxiaolei 从数据中获取配置,确保通过 ffmpeg 的推流,也可以录像
Json::Value tProxyData = searchChannel(_mediaInfo._vhost,_mediaInfo._app,_mediaInfo._streamid);
if(!tProxyData.isNull()) {
int vRecordMp4 = tProxyData["record_mp4"].asInt();
bool vEnableHls = tProxyData.get("enable_hls",false).asBool();
_pPublisherSrc.reset(new RtmpToRtspMediaSource(_mediaInfo._vhost,_mediaInfo._app,_mediaInfo._streamid,vEnableHls, vRecordMp4));
}else{
_pPublisherSrc.reset(new RtmpToRtspMediaSource(_mediaInfo._vhost,_mediaInfo._app,_mediaInfo._streamid));
}
_pPublisherSrc->setListener(dynamic_pointer_cast<MediaSourceEvent>(shared_from_this()));
//如果是rtmp推流客户端那么加大TCP接收缓存这样能提升接收性能
_sock->setReadBuffer(std::make_shared<BufferRaw>(256 * 1024));

View File

@ -53,9 +53,10 @@ public:
const string &app,
const string &id,
bool bEnableHls = true,
bool bEnableMp4 = false,
//chenxiaolei 修改为int, 录像最大录制天数,0就是不录
int bRecordMp4 = 0,
int ringSize = 0):RtmpMediaSource(vhost, app, id,ringSize){
_recorder = std::make_shared<MediaRecorder>(vhost, app, id, bEnableHls, bEnableMp4);
_recorder = std::make_shared<MediaRecorder>(vhost, app, id, bEnableHls, bRecordMp4);
_rtmpDemuxer = std::make_shared<RtmpDemuxer>();
}
virtual ~RtmpToRtspMediaSource(){}

View File

@ -266,6 +266,7 @@ void RtspPusher::sendSetup(unsigned int trackIndex) {
}
}
void RtspPusher::handleResSetup(const Parser &parser, unsigned int uiTrackIndex) {
if (parser.Url() != "200") {
throw std::runtime_error(

View File

@ -36,6 +36,10 @@
#include "Util/TimeTicker.h"
#include "Util/NoticeCenter.h"
#include "Network/sockutil.h"
#include "Kf/Globals.h"
#include "Kf/DbUtil.h"
#include <jsoncpp/value.h>
#include <jsoncpp/json.h>
#define RTSP_SERVER_SEND_RTCP 0
@ -246,7 +250,16 @@ void RtspSession::handleReq_ANNOUNCE(const Parser &parser) {
_strSdp = parser.Content();
_aTrackInfo = SdpParser(_strSdp).getAvailableTrack();
_pushSrc = std::make_shared<RtspToRtmpMediaSource>(_mediaInfo._vhost,_mediaInfo._app,_mediaInfo._streamid);
//chenxiaolei 从数据中获取配置,确保通过 ffmpeg 的推流,也可以录像
Json::Value tProxyData = searchChannel(_mediaInfo._vhost,_mediaInfo._app,_mediaInfo._streamid);
if(!tProxyData.isNull()) {
int vRecordMp4 = tProxyData["record_mp4"].asInt();
bool vEnableHls = tProxyData.get("enable_hls",false).asBool();
_pushSrc = std::make_shared<RtspToRtmpMediaSource>(_mediaInfo._vhost,_mediaInfo._app,_mediaInfo._streamid,vEnableHls,vRecordMp4);
}else{
_pushSrc = std::make_shared<RtspToRtmpMediaSource>(_mediaInfo._vhost,_mediaInfo._app,_mediaInfo._streamid);
}
_pushSrc->setListener(dynamic_pointer_cast<MediaSourceEvent>(shared_from_this()));
_pushSrc->onGetSDP(_strSdp);
sendRtspResponse("200 OK");

View File

@ -46,9 +46,10 @@ public:
const string &app,
const string &id,
bool bEnableHls = true,
bool bEnableMp4 = false,
//chenxiaolei 修改为int, 录像最大录制天数,0就是不录
int bRecordMp4 = 0,
int ringSize = 0) : RtspMediaSource(vhost, app, id,ringSize) {
_recorder = std::make_shared<MediaRecorder>(vhost, app, id, bEnableHls, bEnableMp4);
_recorder = std::make_shared<MediaRecorder>(vhost, app, id, bEnableHls, bRecordMp4);
}
virtual ~RtspToRtmpMediaSource() {}