付智勇

添加agora

  1 +[repoFolder:AgoraRTCEngine, branch:release/1.12.0, commit:89171d96ae055f7809ad9b455256272eee0a0b43]
  2 +[repoFolder:media_sdk2, branch:release/1.12.0, commit:a442f9afe9b468f3f14930db4f3c01f73de90ea3]
  3 +[repoFolder:ServerSDK-Video, branch:release/1.12.0, commit:a4f0d4c22aa9d93b9cd0f71967d4f4b763e46190]
  1 +#pragma once
  2 +
  3 +namespace agora {
  4 +namespace recording {
  5 +
  6 +typedef unsigned char uchar_t;
  7 +typedef unsigned int uint_t;
  8 +typedef unsigned int uid_t;
  9 +
  10 +enum ERROR_CODE_TYPE {
  11 + ERR_OK = 0,
  12 + //1~1000
  13 + ERR_FAILED = 1,
  14 + ERR_INVALID_ARGUMENT = 2,
  15 + ERR_INTERNAL_FAILED = 3,
  16 +};
  17 +
  18 +enum WARN_CODE_TYPE {
  19 + WARN_NO_AVAILABLE_CHANNEL = 103,
  20 + WARN_LOOKUP_CHANNEL_TIMEOUT = 104,
  21 + WARN_LOOKUP_CHANNEL_REJECTED = 105,
  22 + WARN_OPEN_CHANNEL_TIMEOUT = 106,
  23 + WARN_OPEN_CHANNEL_REJECTED = 107,
  24 +};
  25 +
  26 +enum CHANNEL_PROFILE_TYPE
  27 +{
  28 + CHANNEL_PROFILE_COMMUNICATION = 0,
  29 + CHANNEL_PROFILE_LIVE_BROADCASTING = 1,
  30 +};
  31 +
  32 +enum USER_OFFLINE_REASON_TYPE
  33 +{
  34 + USER_OFFLINE_QUIT = 0,
  35 + USER_OFFLINE_DROPPED = 1,
  36 + USER_OFFLINE_BECOME_AUDIENCE = 2,
  37 +};
  38 +
  39 +enum REMOTE_VIDEO_STREAM_TYPE
  40 +{
  41 + REMOTE_VIDEO_STREAM_HIGH = 0,
  42 + REMOTE_VIDEO_STREAM_LOW = 1,
  43 +};
  44 +
  45 +
  46 +typedef struct VideoMixingLayout
  47 +{
  48 + struct Region {
  49 + uid_t uid;
  50 + double x;//[0,1]
  51 + double y;//[0,1]
  52 + double width;//[0,1]
  53 + double height;//[0,1]
  54 + int zOrder; //optional, [0, 100] //0 (default): bottom most, 100: top most
  55 +
  56 + // Optional
  57 + // [0, 1.0] where 0 denotes throughly transparent, 1.0 opaque
  58 + double alpha;
  59 +
  60 + int renderMode;//RENDER_MODE_HIDDEN: Crop, RENDER_MODE_FIT: Zoom to fit
  61 + Region()
  62 + :uid(0)
  63 + , x(0)
  64 + , y(0)
  65 + , width(0)
  66 + , height(0)
  67 + , zOrder(0)
  68 + , alpha(1.0)
  69 + , renderMode(1)
  70 + {}
  71 +
  72 + };
  73 + int canvasWidth;
  74 + int canvasHeight;
  75 + const char* backgroundColor;//e.g. "#C0C0C0" in RGB
  76 + int regionCount;
  77 + const Region* regions;
  78 + const char* appData;
  79 + int appDataLength;
  80 + VideoMixingLayout()
  81 + :canvasWidth(0)
  82 + , canvasHeight(0)
  83 + , backgroundColor(NULL)
  84 + , regionCount(0)
  85 + , regions(NULL)
  86 + , appData(NULL)
  87 + , appDataLength(0)
  88 + {}
  89 +} VideoMixingLayout;
  90 +
  91 +typedef struct UserJoinInfos {
  92 + const char* recordingDir;
  93 + //new attached info add below
  94 +
  95 + UserJoinInfos():
  96 + recordingDir(NULL)
  97 + {}
  98 +}UserJoinInfos;
  99 +
  100 +
  101 +class IRecordingEngineEventHandler {
  102 +public:
  103 + virtual ~IRecordingEngineEventHandler() {}
  104 +
  105 + virtual void onError(int error) = 0;
  106 + virtual void onWarning(int warn) = 0;
  107 +
  108 + virtual void onJoinChannelSuccess(const char * channelId, uid_t uid) = 0;
  109 + virtual void onLeaveChannel() = 0;
  110 +
  111 + virtual void onUserJoined(uid_t uid, UserJoinInfos &infos) = 0;
  112 + virtual void onUserOffline(uid_t uid, USER_OFFLINE_REASON_TYPE reason) = 0;
  113 +};
  114 +
  115 +typedef struct RecordingConfig {
  116 + CHANNEL_PROFILE_TYPE channelProfile;
  117 + bool isAudioOnly;
  118 + bool isMixingEnabled;
  119 + char * decryptionMode;
  120 + char * secret;
  121 + int idleLimitSec;
  122 + char * appliteDir;
  123 +// char * appliteLogDir;
  124 + char * recordFileRootDir;
  125 + int lowUdpPort;
  126 + int highUdpPort;
  127 +
  128 + RecordingConfig(): channelProfile(CHANNEL_PROFILE_COMMUNICATION),
  129 + isAudioOnly(false),
  130 + isMixingEnabled(false),
  131 + decryptionMode(NULL),
  132 + secret(NULL),
  133 + idleLimitSec(300),
  134 + appliteDir(NULL),
  135 +// appliteLogDir(NULL),
  136 + recordFileRootDir(NULL),
  137 + lowUdpPort(0),
  138 + highUdpPort(0)
  139 + {}
  140 +} RecordingConfig;
  141 +
  142 +class IRecordingEngine{
  143 +public:
  144 +
  145 + /**
  146 + * create a new recording engine instance
  147 + *
  148 + * @param appId The App ID issued to the application developers by Agora.io.
  149 + * @param eventHandler the callback interface
  150 + *
  151 + * @return a recording engine instance pointer
  152 + */
  153 + static IRecordingEngine* createAgoraRecordingEngine(const char * appId, IRecordingEngineEventHandler *eventHandler);
  154 +
  155 + virtual ~IRecordingEngine() {}
  156 +
  157 + /**
  158 + * This method lets the recording engine join a channel, and start recording
  159 + *
  160 + * @param channelKey This parameter is optional if the user uses a static key, or App ID. In this case, pass NULL as the parameter value. More details refer to http://docs-origin.agora.io/en/user_guide/Component_and_Others/Dynamic_Key_User_Guide.html
  161 + * @param channelId A string providing the unique channel id for the AgoraRTC session
  162 + * @param uid The uid of recording client
  163 + * @param config The config of current recording
  164 + *
  165 + * @return 0: Method call succeeded. <0: Method call failed.
  166 + */
  167 + virtual int joinChannel(const char * channelKey, const char *channelId, uid_t uid, const RecordingConfig &config) = 0;
  168 +
  169 +
  170 +
  171 + /**
  172 + * set the layout of video mixing
  173 + *
  174 + * @param layout layout setting
  175 + *
  176 + * @return 0: Method call succeeded. <0: Method call failed.
  177 + */
  178 + virtual int setVideoMixingLayout(const VideoMixingLayout &layout) = 0;
  179 +
  180 + /**
  181 + * Stop recording
  182 + *
  183 + * @return 0: Method call succeeded. <0: Method call failed.
  184 + */
  185 + virtual int leaveChannel() = 0;
  186 +
  187 + /**
  188 + * release recording engine
  189 + *
  190 + * @return 0: Method call succeeded. <0: Method call failed.
  191 + */
  192 + virtual int release() = 0;
  193 +};
  194 +
  195 +}
  196 +}
  1 +#pragma once
  2 +
  3 +#ifdef __GNUC__
  4 +#if __GNUC_PREREQ(4, 6)
  5 +#include <atomic>
  6 +typedef std::atomic<bool> atomic_bool_t;
  7 +
  8 +#elif __GNUC_PREREQ(4, 4)
  9 +namespace agora {
  10 +namespace base {
  11 +
  12 +template <typename T> class atomic;
  13 +
  14 +template <>
  15 +class atomic<bool> {
  16 + public:
  17 + explicit atomic(bool value=false);
  18 + ~atomic();
  19 +
  20 + operator bool() const;
  21 + atomic<bool>& operator=(bool value);
  22 + bool load();
  23 + void store(bool value);
  24 + private:
  25 + bool value_;
  26 +};
  27 +
  28 +inline
  29 +atomic<bool>::atomic(bool value) {
  30 + value_ = value;
  31 + __sync_synchronize();
  32 +}
  33 +
  34 +inline
  35 +atomic<bool>::~atomic() {
  36 +}
  37 +
  38 +inline
  39 +atomic<bool>::operator bool() const {
  40 + return value_;
  41 +}
  42 +
  43 +inline
  44 +atomic<bool>& atomic<bool>::operator=(bool value) {
  45 + value_ = value;
  46 + __sync_synchronize();
  47 + return *this;
  48 +}
  49 +
  50 +inline bool atomic<bool>::load() {
  51 + __sync_synchronize();
  52 + return value_;
  53 +}
  54 +
  55 +inline void atomic<bool>::store(bool value) {
  56 + value_ = value;
  57 + __sync_synchronize();
  58 +}
  59 +
  60 +}
  61 +}
  62 +
  63 +typedef agora::base::atomic<bool> atomic_bool_t;
  64 +#else
  65 +#error "version should be at least 4.4"
  66 +//__sync_synchronize() doesn't produce mfence instruction
  67 +//when gcc version less than 4.4
  68 +#endif
  69 +#endif
  1 +#pragma once
  2 +
  3 +#include <sys/types.h>
  4 +#include <unistd.h>
  5 +
  6 +#include <cinttypes>
  7 +#include <cstdint>
  8 +#include <cstdlib>
  9 +#include <stdarg.h>
  10 +#include <cassert>
  11 +#include <syslog.h>
  12 +
  13 +namespace agora {
  14 +namespace base {
  15 +
  16 +enum log_levels {
  17 + DEBUG_LOG = LOG_DEBUG, /* 7 debug-level messages */
  18 + INFO_LOG = LOG_INFO, /* 6 informational */
  19 + NOTICE_LOG = LOG_NOTICE, /* 5 normal but significant condition */
  20 + WARN_LOG = LOG_WARNING, /* 4 warning conditions */
  21 + ERROR_LOG = LOG_ERR, /* 3 error conditions */
  22 + FATAL_LOG = LOG_CRIT, /* 2 critical conditions */
  23 +};
  24 +
  25 +struct log_config {
  26 + static int enabled_level;
  27 + static uint64_t dropped_count;
  28 +
  29 + static uint32_t drop_cancel;
  30 + const static uint32_t DROP_COUNT = 1000;
  31 +
  32 + static inline void enable_debug(bool enabled) {
  33 + if (enabled) {
  34 + log_config::enabled_level = DEBUG_LOG;
  35 + } else {
  36 + log_config::enabled_level = INFO_LOG;
  37 + }
  38 + }
  39 +
  40 + static bool set_drop_cannel(uint32_t cancel) {
  41 + if (cancel > DROP_COUNT) {
  42 + drop_cancel = DROP_COUNT;
  43 + return false;
  44 + }
  45 +
  46 + drop_cancel = cancel;
  47 + return true;
  48 + }
  49 +
  50 + static inline bool log_enabled(log_levels level) {
  51 + if (level <= enabled_level) {
  52 + return true;
  53 + }
  54 +
  55 + ++dropped_count;
  56 + return (dropped_count % DROP_COUNT < drop_cancel);
  57 + }
  58 +};
  59 +
  60 +inline void open_log() {
  61 + ::openlog(NULL, LOG_PID|LOG_NDELAY, LOG_USER|LOG_DAEMON);
  62 +}
  63 +
  64 +inline void log(log_levels level, const char* format, ...) {
  65 + if (! log_config::log_enabled(level)) {
  66 + return;
  67 + }
  68 +
  69 + va_list args;
  70 + va_start(args, format);
  71 + ::vsyslog(level, format, args);
  72 + va_end(args);
  73 +}
  74 +
  75 +inline void close_log() {
  76 + ::closelog();
  77 +}
  78 +
  79 +}
  80 +}
  81 +
  82 +#define LOG(level, fmt, ...) log(agora::base::level ## _LOG, \
  83 + "(%d) %s:%d: " fmt, getpid(), __FILE__, __LINE__, ##__VA_ARGS__)
  84 +
  85 +#define LOG_IF(level, cond, ...) \
  86 + if (cond) { \
  87 + LOG(level, __VA_ARGS__); \
  88 + }
  89 +
  90 +#define LOG_EVERY_N(level, N, ...) \
  91 + { \
  92 + static unsigned int count = 0; \
  93 + if (++count % N == 0) \
  94 + LOG(level, __VA_ARGS__); \
  95 + }
  1 +ifeq (${CXX},)
  2 +CXX=g++
  3 +endif
  4 +LINK=${CXX}
  5 +
  6 +TOPDIR=`pwd`/..
  7 +LIBPATH=${TOPDIR}/libs
  8 +#-static-libstdc++
  9 +LDFLAGS= -static-libgcc -std=c++11
  10 +CXXFLAGS = -pipe -std=c++0x -fPIC -g -fno-omit-frame-pointer \
  11 + -DNDEBUG=1 -Wconversion -O3 -Wall -W -fvisibility=hidden
  12 +
  13 +LIB = -pthread -lpthread -L$(LIBPATH) -lRecordEngine -lrt
  14 +INCPATH =-I. -I${TOPDIR}/include
  15 +
  16 +SRC =$(wildcard *.cpp)
  17 +OBJ=$(addsuffix .o, $(basename $(SRC)))
  18 +TARGET=Recorder_local
  19 +
  20 +
  21 +.PHONY: all clean
  22 +all: $(TARGET)
  23 +
  24 +$(TARGET): $(OBJ)
  25 + $(LINK) $(LDFLAGS) $(INCPATH) $^ -o "$@" $(LIB)
  26 +
  27 +%.o: %.cpp
  28 + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<"
  29 +
  30 +clean:
  31 + rm -f $(TARGET)
  32 + rm -f ${OBJ}
  1 +#pragma once
  2 +
  3 +#include <cstdint>
  4 +#include <iostream>
  5 +#include <string>
  6 +#include <unordered_map>
  7 +
  8 +namespace agora {
  9 +namespace base {
  10 +struct ipv4 {
  11 + union {
  12 + uint32_t ip;
  13 + uint8_t repr[4];
  14 + };
  15 +};
  16 +
  17 +struct mac_addr {
  18 + uint8_t addr_bytes[6];
  19 +};
  20 +
  21 +class opt_parser {
  22 + public:
  23 + enum opt_type {
  24 + no_argu=0,
  25 + require_argu=1,
  26 + opt_argu=2,
  27 + };
  28 +
  29 + private:
  30 + enum pointer_type {kBool, kInt32, kUInt32, kInt64, kUInt64, kDouble, kString,
  31 + kIPv4, kMacAddr};
  32 +
  33 + struct internal_opt {
  34 + pointer_type type;
  35 + union {
  36 + bool *bool_ptr;
  37 + int32_t *int32_ptr;
  38 + uint32_t *uint32_ptr;
  39 + int64_t *int64_ptr;
  40 + uint64_t *uint64_ptr;
  41 + double *double_ptr;
  42 + std::string *string_ptr;
  43 + ipv4 *ipv4_ptr;
  44 + mac_addr *addr_ptr;
  45 + };
  46 + const char *help;
  47 + opt_type optType;
  48 + };
  49 + public:
  50 + opt_parser() {}
  51 +
  52 + // bool add_short_arg(bool *store, char short_arg);
  53 + // bool add_short_arg(int *store, char short_arg);
  54 + // bool add_short_arg(std::string *store, char short_arg);
  55 +
  56 + bool add_long_opt(const char *long_opt, bool *store, const char *help,
  57 + opt_parser::opt_type optParamType = opt_parser::opt_argu);
  58 + bool add_long_opt(const char *long_opt, int32_t *store, const char *help,
  59 + opt_parser::opt_type optParamType = opt_parser::opt_argu);
  60 + bool add_long_opt(const char *long_opt, uint32_t *store, const char *help,
  61 + opt_parser::opt_type optParamType = opt_parser::opt_argu);
  62 + bool add_long_opt(const char *long_opt, int64_t *store, const char *help,
  63 + opt_parser::opt_type optParamType = opt_parser::opt_argu);
  64 + bool add_long_opt(const char *long_opt, uint64_t *store, const char *help,
  65 + opt_parser::opt_type optParamType = opt_parser::opt_argu);
  66 + bool add_long_opt(const char *long_opt, double *store, const char *help,
  67 + opt_parser::opt_type optParamType = opt_parser::opt_argu);
  68 + bool add_long_opt(const char *long_opt, std::string *store, const char *help,
  69 + opt_parser::opt_type optParamType = opt_parser::opt_argu);
  70 + bool add_long_opt(const char *long_opt, ipv4 *store, const char *help,
  71 + opt_parser::opt_type optParamType = opt_parser::opt_argu);
  72 + bool add_long_opt(const char *long_opt, mac_addr *store, const char *help,
  73 + opt_parser::opt_type optParamType = opt_parser::opt_argu);
  74 +
  75 + // NOTE(liuyong): the FIRST argument must be supplied as a place holder
  76 + bool parse_opts(int argc, char *const argv[]);
  77 +
  78 + void clear();
  79 +
  80 + void print_usage(const char *exec_file, std::ostream &sout) const;
  81 + private:
  82 + // bool insert_short_arg(internal_opt arg, char short_arg);
  83 + bool insert_long_opt(internal_opt &opt, const char *long_opt);
  84 +
  85 + static bool fill_arg(const char *opt_name, const internal_opt &opt,
  86 + const char *opt_arg);
  87 +
  88 + static bool parse_ipv4(const char *arg, ipv4 *ip);
  89 + static bool parse_mac_addr(const char *arg, mac_addr *addr);
  90 + private:
  91 + // std::unordered_map<char, internal_opt> short_args_;
  92 + std::unordered_map<const char *, internal_opt> long_opts_;
  93 +};
  94 +
  95 +}
  96 +}
  97 +
  1 +#include <csignal>
  2 +#include <cstdint>
  3 +#include <iostream>
  4 +#include <sstream>
  5 +#include <string>
  6 +#include <vector>
  7 +#include <algorithm>
  8 +
  9 +#include "IAgoraRecordingEngine.h"
  10 +
  11 +#include "base/atomic.h"
  12 +#include "base/log.h"
  13 +#include "base/opt_parser.h"
  14 +
  15 +using std::string;
  16 +using std::cout;
  17 +using std::cerr;
  18 +using std::endl;
  19 +
  20 +using agora::base::opt_parser;
  21 +
  22 +class AgoraRecorder : public agora::recording::IRecordingEngineEventHandler {
  23 + public:
  24 + // appId
  25 + AgoraRecorder();
  26 + ~AgoraRecorder();
  27 +
  28 + bool createChannel(const string &appid, const string &channelKey, const string &name, uid_t uid,
  29 + bool decodeAudio, bool decodeVideo, agora::recording::RecordingConfig &config);
  30 +
  31 + int setVideoMixLayout();
  32 +
  33 + bool leaveChannel();
  34 + bool release();
  35 +
  36 + bool stopped() const;
  37 +
  38 + private:
  39 + virtual void onError(int error);
  40 + virtual void onWarning(int warn);
  41 +
  42 + virtual void onJoinChannelSuccess(const char * channelId, uid_t uid);
  43 + virtual void onLeaveChannel();
  44 +
  45 + virtual void onUserJoined(uid_t uid, agora::recording::UserJoinInfos &infos);
  46 + virtual void onUserOffline(uid_t uid, agora::recording::USER_OFFLINE_REASON_TYPE reason);
  47 +
  48 + private:
  49 + atomic_bool_t m_stopped;
  50 + agora::recording::IRecordingEngine *m_recorder;
  51 +
  52 + std::vector<agora::recording::uid_t> m_peers;
  53 +};
  54 +
  55 +AgoraRecorder::AgoraRecorder(): IRecordingEngineEventHandler() {
  56 + m_recorder = NULL;
  57 + m_stopped.store(false);
  58 +}
  59 +
  60 +AgoraRecorder::~AgoraRecorder() {
  61 + if (m_recorder) {
  62 + m_recorder->release();
  63 + }
  64 +}
  65 +
  66 +bool AgoraRecorder::stopped() const {
  67 + return m_stopped;
  68 +}
  69 +
  70 +bool AgoraRecorder::release() {
  71 + if (m_recorder) {
  72 + m_recorder->release();
  73 + m_recorder = NULL;
  74 + }
  75 +
  76 + return true;
  77 +}
  78 +
  79 +bool AgoraRecorder::createChannel(const string &appid, const string &channelKey, const string &name,
  80 + uint32_t uid,
  81 + bool decodeAudio, bool decodeVideo,
  82 + agora::recording::RecordingConfig &config)
  83 +{
  84 + if ((m_recorder = agora::recording::IRecordingEngine::createAgoraRecordingEngine(appid.c_str(), this)) == NULL)
  85 + return false;
  86 +
  87 +
  88 + return 0 == m_recorder->joinChannel(channelKey.c_str(), name.c_str(), uid, config);
  89 +}
  90 +
  91 +bool AgoraRecorder::leaveChannel() {
  92 + if (m_recorder) {
  93 + m_recorder->leaveChannel();
  94 + m_stopped = true;
  95 + }
  96 +
  97 + return true;
  98 +}
  99 +
  100 +int AgoraRecorder::setVideoMixLayout()
  101 +{
  102 + LOG(INFO, "setVideoMixLayout: user size: %d", m_peers.size());
  103 +
  104 + agora::recording::VideoMixingLayout layout;
  105 + layout.canvasWidth = 360;
  106 + layout.canvasHeight = 640;
  107 + layout.backgroundColor = "#23b9dc";
  108 +
  109 + layout.regionCount = m_peers.size();
  110 +
  111 + if (!m_peers.empty()) {
  112 + LOG(INFO, "setVideoMixLayout: peers not empty");
  113 + agora::recording::VideoMixingLayout::Region * regionList = new agora::recording::VideoMixingLayout::Region[m_peers.size()];
  114 +
  115 + regionList[0].uid = m_peers[0];
  116 + regionList[0].x = 0.f;
  117 + regionList[0].y = 0.f;
  118 + regionList[0].width = 1.f;
  119 + regionList[0].height = 1.f;
  120 + regionList[0].zOrder = 0;
  121 + regionList[0].alpha = 1.f;
  122 + regionList[0].renderMode = 0;
  123 +
  124 + LOG(INFO, "region 0 uid: %d, x: %f, y: %f, width: %f, height: %f, zOrder: %d, alpha: %f", regionList[0].uid, regionList[0].x, regionList[0].y, regionList[0].width, regionList[0].height, regionList[0].zOrder, regionList[0].alpha);
  125 +
  126 +
  127 + float canvasWidth = 360.0;
  128 + float canvasHeight = 640.0;
  129 +
  130 + float viewWidth = 0.3;
  131 + float viewHEdge = 0.025;
  132 + float viewHeight = viewWidth * (canvasWidth / canvasHeight);
  133 + float viewVEdge = viewHEdge * (canvasWidth / canvasHeight);
  134 +
  135 + for (int i=1; i<m_peers.size(); i++) {
  136 + if (i >= 7)
  137 + break;
  138 +
  139 + regionList[i].uid = m_peers[i];
  140 +
  141 + float xIndex = i % 3;
  142 + float yIndex = i / 3;
  143 + regionList[i].x = xIndex * (viewWidth + viewHEdge) + viewHEdge;
  144 + regionList[i].y = 1 - (yIndex + 1) * (viewHeight + viewVEdge);
  145 + regionList[i].width = viewWidth;
  146 + regionList[i].height = viewHeight;
  147 + regionList[i].zOrder = 0;
  148 + regionList[i].alpha = i + 1;
  149 + regionList[i].renderMode = 0;
  150 + }
  151 +
  152 + layout.regions = regionList;
  153 +// LOG(INFO, "region 0 uid: %d, x: %f, y: %f, width: %f, height: %f, zOrder: %d, alpha: %f", regionList[0].uid, regionList[0].x, regionList[0].y, regionList[0].width, regionList[0].height, regionList[0].zOrder, regionList[0].alpha);
  154 + }
  155 + else {
  156 + layout.regions = NULL;
  157 + }
  158 +
  159 + m_recorder->setVideoMixingLayout(layout);
  160 +}
  161 +
  162 +void AgoraRecorder::onError(int error) {
  163 + cerr << "Error: " << error << endl;
  164 + leaveChannel();
  165 +}
  166 +
  167 +void AgoraRecorder::onWarning(int warn) {
  168 + cerr << "warn: " << warn << endl;
  169 + // leaveChannel();
  170 +}
  171 +
  172 +void AgoraRecorder::onJoinChannelSuccess(const char * channelId, uid_t uid) {
  173 + cout << "join channel Id: " << channelId << ", with uid: " << uid << endl;
  174 +}
  175 +
  176 +void AgoraRecorder::onLeaveChannel() {
  177 + cout << "leave channel" << endl;
  178 +}
  179 +
  180 +void AgoraRecorder::onUserJoined(unsigned uid, agora::recording::UserJoinInfos &infos) {
  181 + cout << "User " << uid << " joined, RecordingDir:" << (infos.recordingDir? infos.recordingDir:"NULL") <<endl;
  182 + m_peers.push_back(uid);
  183 +
  184 + setVideoMixLayout();
  185 +}
  186 +
  187 +void AgoraRecorder::onUserOffline(unsigned uid, agora::recording::USER_OFFLINE_REASON_TYPE reason) {
  188 + cout << "User " << uid << " offline, reason: " << reason << endl;
  189 + m_peers.erase(std::remove(m_peers.begin(), m_peers.end(), uid), m_peers.end());
  190 +
  191 + setVideoMixLayout();
  192 +}
  193 +
  194 +atomic_bool_t g_bSignalStop;
  195 +
  196 +void signal_handler(int signo) {
  197 + (void)signo;
  198 +
  199 + // cerr << "Signal " << signo << endl;
  200 + g_bSignalStop = true;
  201 +}
  202 +
  203 +int main(int argc, char * const argv[]) {
  204 + uint32_t uid = 0;
  205 + string appId;
  206 + string channelKey;
  207 + string name;
  208 + bool decodeAudio = false;
  209 + bool decodeVideo = false;
  210 +
  211 + uint32_t channelProfile;
  212 +
  213 + string decryptionMode;
  214 + string secret;
  215 +
  216 + int idleLimitSec=30*60;//30min
  217 +
  218 + string applitePath;
  219 + string appliteLogPath;
  220 + string recordFileRootDir=".";
  221 +
  222 + int lowUdpPort = 0;//40000;
  223 + int highUdpPort = 0;//40004;
  224 +
  225 + bool isAudioOnly=0;
  226 + bool isMixingEnabled=0;
  227 +
  228 +
  229 + g_bSignalStop = false;
  230 + signal(SIGQUIT, signal_handler);
  231 + signal(SIGABRT, signal_handler);
  232 + signal(SIGINT, signal_handler);
  233 + signal(SIGPIPE, SIG_IGN);
  234 +
  235 + opt_parser parser;
  236 +
  237 +
  238 +
  239 + parser.add_long_opt("appId", &appId, "App Id/must", agora::base::opt_parser::require_argu);
  240 + parser.add_long_opt("uid", &uid, "User Id default is 0/must", agora::base::opt_parser::require_argu);
  241 +
  242 + parser.add_long_opt("channel", &name, "Channel Id/must", agora::base::opt_parser::require_argu);
  243 + parser.add_long_opt("appliteDir", &applitePath, "directory of app lite 'video_recorder', Must pointer to 'Agora_Recording_SDK_for_Linux_FULL/bin/' folder/must",
  244 + agora::base::opt_parser::require_argu);
  245 +
  246 + parser.add_long_opt("channelKey", &channelKey, "channelKey/option");
  247 + parser.add_long_opt("channelProfile", &channelProfile, "channel_profile:(0:COMMUNICATION),(1:broadcast) default is 0/option");
  248 +
  249 + parser.add_long_opt("isAudioOnly", &isAudioOnly, "Default 0:ARS (0:1)/option");
  250 + parser.add_long_opt("isMixingEnabled", &isMixingEnabled, "Mixing Enable? (0:1)/option");
  251 +
  252 + parser.add_long_opt("decryptionMode", &decryptionMode, "decryption Mode, default is NULL/option");
  253 + parser.add_long_opt("secret", &secret, "input secret when enable decryptionMode/option");
  254 +
  255 + parser.add_long_opt("idle", &idleLimitSec, "Default 300s/option");
  256 + parser.add_long_opt("recordFileRootDir", &recordFileRootDir, "recording file root dir/option");
  257 +
  258 + parser.add_long_opt("lowUdpPort", &lowUdpPort, "default is random value/option");
  259 + parser.add_long_opt("highUdpPort", &highUdpPort, "default is random value/option");
  260 +
  261 +
  262 + if (!parser.parse_opts(argc, argv) || appId.empty() || name.empty()) {
  263 + std::string usage = "Usage: \n\
  264 + ./RECORD_APP --appId STRING --uid UINTEGER32 --channel STRING --appliteDir STRING --channelKey STRING --channelProfile UINTEGER32 --isAudioOnly 0/1 --isMixingEnabled 0/1 --decryptionMode STRING --secret STRING --idle INTEGER32 --recordFileRootDir STRING --lowUdpPort INTEGER32 --highUdpPort INTEGER32\n \
  265 + --appId (App Id/must) \n \
  266 + --uid (User Id default is 0/must) \n \
  267 + --channel (Channel Id/must) \n \
  268 + --appliteDir (directory of app lite 'video_recorder', Must pointer to 'Agora_Recording_SDK_for_Linux_FULL/bin/' folder/must) \n \
  269 + --channelKey (channelKey/option) \n \
  270 + --channelProfile (channel_profile:(0:COMMUNICATION),(1:broadcast) default is 0/option) \n \
  271 + --isAudioOnly (Default 0:ARS (0:1)/option) \n \
  272 + --isMixingEnabled (Mixing Enable? (0:1)/option) \n \
  273 + --decryptionMode (decryption Mode, default is NULL/option) \n \
  274 + --secret (input secret when enable decryptionMode/option) \n \
  275 + --idle (Default 300s/option) \n \
  276 + --recordFileRootDir (recording file root dir/option) \n \
  277 + --lowUdpPort (default is random value/option) \n \
  278 + --highUdpPort (default is random value/option)";
  279 +
  280 + std::cerr << usage << std::endl;
  281 + return -1;
  282 + }
  283 +
  284 + LOG(INFO, "uid %" PRIu32 " from vendor %s is joining channel %s",
  285 + uid, appId.c_str(), name.c_str());
  286 +
  287 + AgoraRecorder recorder;
  288 + agora::recording::RecordingConfig config;
  289 + config.idleLimitSec = idleLimitSec;
  290 + config.channelProfile = static_cast<agora::recording::CHANNEL_PROFILE_TYPE>(channelProfile);
  291 +
  292 + config.isAudioOnly = isAudioOnly;
  293 + config.isMixingEnabled = isMixingEnabled;
  294 +
  295 + config.appliteDir = const_cast<char*>(applitePath.c_str());
  296 + config.recordFileRootDir = const_cast<char*>(recordFileRootDir.c_str());
  297 +
  298 + config.secret = secret.empty()? NULL:const_cast<char*>(secret.c_str());
  299 + config.decryptionMode = decryptionMode.empty()? NULL:const_cast<char*>(decryptionMode.c_str());
  300 +
  301 + config.lowUdpPort = lowUdpPort;
  302 + config.highUdpPort = highUdpPort;
  303 +
  304 +
  305 + if (!recorder.createChannel(appId, channelKey, name, uid, decodeAudio, decodeVideo, config)) {
  306 + cerr << "Failed to create agora channel: " << name << endl;
  307 + return -1;
  308 + }
  309 +
  310 + while (!recorder.stopped() && !g_bSignalStop) {
  311 + sleep(1);
  312 + }
  313 +
  314 + if (g_bSignalStop) {
  315 + recorder.leaveChannel();
  316 + recorder.release();
  317 + }
  318 +
  319 + cerr << "Stopped \n";
  320 + return 0;
  321 +}
  322 +
  1 +#include <cassert>
  2 +#include <cctype>
  3 +#include <cstdlib>
  4 +#include <cstring>
  5 +#include <iostream>
  6 +#include <vector>
  7 +#include <cstring>
  8 +
  9 +#if (defined(_WIN32) || defined(_WIN64)) && !defined(__GNUC__)
  10 +enum {ERROR=-1, INFO=0, WARNING, FATAL};
  11 +#define LOG(level, fmt, ...) fprintf(stderr, #level fmt "\n", __VA_ARGS__)
  12 +#define strtoll _strtoi64
  13 +#define strtoull _strtoui64
  14 +#else
  15 +#include <getopt.h>
  16 +#include "base/log.h"
  17 +#endif
  18 +
  19 +#include "base/opt_parser.h"
  20 +
  21 +using namespace std;
  22 +
  23 +namespace agora {
  24 +namespace base {
  25 +#if defined(_WIN32) && !defined(__GNUC__)
  26 +
  27 +enum {no_argument = 0, required_argument, optional_argument};
  28 +
  29 +int optind = 0;
  30 +const char *optarg = NULL;
  31 +
  32 +struct option {
  33 + const char *name;
  34 + int has_arg;
  35 + int *flag;
  36 + int val;
  37 +};
  38 +
  39 +int getopt_long_only(int argc, char *const argv[], const char *short_opts,
  40 + const option *long_opts, int *index) {
  41 + if (short_opts && *short_opts != '\0') {
  42 + LOG(ERROR, "short options have not been implemented yet!");
  43 + return -1;
  44 + }
  45 +
  46 + optind = optind == 0 ? 1 : optind;
  47 + optarg = NULL;
  48 +
  49 + if (optind >= argc)
  50 + return -1;
  51 +
  52 + const char *a = argv[optind];
  53 + if (a[0] != '-' || a[1] != '-')
  54 + return -1;
  55 +
  56 + a += 2;
  57 +
  58 + int i = 0;
  59 +
  60 + for (; long_opts[i].name != NULL; ++i) {
  61 + if (!strcmp(long_opts[i].name, a)) {
  62 + ++optind;
  63 + *index = i;
  64 + return 0;
  65 + }
  66 + }
  67 +
  68 + return '?';
  69 +}
  70 +
  71 +#endif
  72 +
  73 +// bool opt_parser::insert_short_arg(internal_opt arg, char short_arg) {
  74 +// if (!isalpha(short_arg)) {
  75 +// LOG(ERROR, "short parameters should be alphabetic!");
  76 +// return false;
  77 +// }
  78 +//
  79 +// if (short_args_.count(short_arg) > 0) {
  80 +// LOG(ERROR, "short parameter[%c] has been occupied!", short_arg);
  81 +// return false;
  82 +// }
  83 +//
  84 +// short_args_[short_arg] = arg;
  85 +// return true;
  86 +// }
  87 +
  88 +bool opt_parser::insert_long_opt(internal_opt &opt, const char *long_opt) {
  89 + if (!long_opt) {
  90 + LOG(ERROR, "A full parameter should be supplied!");
  91 + return false;
  92 + }
  93 +
  94 + if (long_opts_.count(long_opt) > 0) {
  95 + LOG(ERROR, "{%s} has been occupied yet!", long_opt);
  96 + return false;
  97 + }
  98 + if(opt.help == NULL || !strlen(opt.help))
  99 + opt.help = "NA";
  100 +
  101 + long_opts_[long_opt] = opt;
  102 + return true;
  103 +}
  104 +
  105 +// bool opt_parser::add_short_arg(bool *store, char short_arg) {
  106 +// *store = false;
  107 +//
  108 +// internal_opt arg = {kBool, {store}};
  109 +// return insert_short_arg(arg, short_arg);
  110 +// }
  111 +//
  112 +// bool opt_parser::add_short_arg(int *store, char short_arg) {
  113 +// internal_opt arg;
  114 +// arg.type = kInt;
  115 +// arg.int_ptr = store;
  116 +//
  117 +// return insert_short_arg(arg, short_arg);
  118 +// }
  119 +//
  120 +// bool opt_parser::add_short_arg(string *store, char short_arg) {
  121 +// internal_opt arg;
  122 +// arg.type = kString;
  123 +// arg.string_ptr = store;
  124 +//
  125 +// return insert_short_arg(arg, short_arg);
  126 +// }
  127 +
  128 +bool opt_parser::add_long_opt(const char *long_opt, bool *store,
  129 + const char *help, opt_parser::opt_type optParamType)
  130 +{
  131 + internal_opt arg;
  132 + arg.type = kBool;
  133 + arg.bool_ptr = store;
  134 + arg.help = help;
  135 + arg.optType = optParamType;
  136 +
  137 + return insert_long_opt(arg, long_opt);
  138 +}
  139 +
  140 +bool opt_parser::add_long_opt(const char *long_opt, int32_t *store,
  141 + const char *help, opt_parser::opt_type optParamType)
  142 +{
  143 + internal_opt arg;
  144 + arg.type = kInt32;
  145 + arg.int32_ptr = store;
  146 + arg.help = help;
  147 + arg.optType = optParamType;
  148 +
  149 + return insert_long_opt(arg, long_opt);
  150 +}
  151 +
  152 +bool opt_parser::add_long_opt(const char *long_opt, uint32_t *store,
  153 + const char *help, opt_parser::opt_type optParamType)
  154 +{
  155 + internal_opt arg;
  156 + arg.type = kUInt32;
  157 + arg.uint32_ptr = store;
  158 + arg.help = help;
  159 + arg.optType = optParamType;
  160 +
  161 + return insert_long_opt(arg, long_opt);
  162 +}
  163 +
  164 +bool opt_parser::add_long_opt(const char *long_opt, int64_t *store,
  165 + const char *help, opt_parser::opt_type optParamType)
  166 +{
  167 + internal_opt arg;
  168 + arg.type = kInt64;
  169 + arg.int64_ptr = store;
  170 + arg.help = help;
  171 + arg.optType = optParamType;
  172 +
  173 + return insert_long_opt(arg, long_opt);
  174 +}
  175 +
  176 +bool opt_parser::add_long_opt(const char *long_opt, uint64_t *store,
  177 + const char *help, opt_parser::opt_type optParamType)
  178 +{
  179 + internal_opt arg;
  180 + arg.type = kUInt64;
  181 + arg.uint64_ptr = store;
  182 + arg.help = help;
  183 + arg.optType = optParamType;
  184 +
  185 + return insert_long_opt(arg, long_opt);
  186 +}
  187 +
  188 +bool opt_parser::add_long_opt(const char *long_opt, double *store,
  189 + const char *help, opt_parser::opt_type optParamType)
  190 +{
  191 + internal_opt arg;
  192 + arg.type = kDouble;
  193 + arg.double_ptr = store;
  194 + arg.help = help;
  195 + arg.optType = optParamType;
  196 +
  197 + return insert_long_opt(arg, long_opt);
  198 +}
  199 +
  200 +bool opt_parser::add_long_opt(const char *long_opt, string *store,
  201 + const char *help, opt_parser::opt_type optParamType)
  202 +{
  203 + internal_opt arg;
  204 + arg.type = kString;
  205 + arg.string_ptr = store;
  206 + arg.help = help;
  207 + arg.optType = optParamType;
  208 +
  209 + return insert_long_opt(arg, long_opt);
  210 +}
  211 +
  212 +bool opt_parser::add_long_opt(const char *long_opt, ipv4 *store,
  213 + const char *help, opt_parser::opt_type optParamType)
  214 +{
  215 + internal_opt arg;
  216 + arg.type = kIPv4;
  217 + arg.ipv4_ptr = store;
  218 + arg.help = help;
  219 + arg.optType = optParamType;
  220 +
  221 + return insert_long_opt(arg, long_opt);
  222 +}
  223 +
  224 +bool opt_parser::add_long_opt(const char *long_opt, mac_addr *store,
  225 + const char *help, opt_parser::opt_type optParamType)
  226 +{
  227 + internal_opt arg;
  228 + arg.type = kMacAddr;
  229 + arg.addr_ptr = store;
  230 + arg.help = help;
  231 + arg.optType = optParamType;
  232 +
  233 + return insert_long_opt(arg, long_opt);
  234 +}
  235 +
  236 +bool opt_parser::parse_ipv4(const char *arg, ipv4 *ip) {
  237 + uint8_t (&a)[4] = ip->repr;
  238 + if (sscanf(arg, "%hhu.%hhu.%hhu.%hhu", &a[0], &a[1], &a[2], &a[3]) != 4) {
  239 + LOG(ERROR, "Illegal IP Format: %s", arg);
  240 + return false;
  241 + }
  242 +
  243 + return true;
  244 +}
  245 +
  246 +bool opt_parser::parse_mac_addr(const char *arg, mac_addr *addr) {
  247 + uint8_t (&b)[6] = addr->addr_bytes;
  248 + if (sscanf(arg, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &b[0], &b[1],
  249 + &b[2], &b[3], &b[4], &b[5]) != 6) {
  250 + LOG(ERROR, "Illegal ethernet physical address: %s", arg);
  251 + return false;
  252 + }
  253 +
  254 + return true;
  255 +}
  256 +
  257 +bool opt_parser::fill_arg(const char *opt_name, const internal_opt &opt,
  258 + const char *opt_arg) {
  259 +
  260 + if (opt_arg == NULL) {
  261 + LOG(ERROR, "No argument available for %s", opt_name);
  262 + return false;
  263 + }
  264 +
  265 + char *end_ptr = NULL;
  266 +
  267 + switch (opt.type) {
  268 + case kBool: {
  269 + unsigned long n = strtoul(opt_arg, &end_ptr, 10);
  270 + if (*end_ptr != '\0') {
  271 + LOG(ERROR, "Invalid integer argument: %s", opt_arg);
  272 + return false;
  273 + }
  274 + *opt.bool_ptr = n? true:false;
  275 + }
  276 + break;
  277 +
  278 + case kInt32: {
  279 + long n = strtol(opt_arg, &end_ptr, 10);
  280 + if (*end_ptr != '\0') {
  281 + LOG(ERROR, "Invalid integer argument: %s", opt_arg);
  282 + return false;
  283 + }
  284 + *opt.int32_ptr = int32_t(n);
  285 + break;
  286 + }
  287 + case kUInt32: {
  288 + unsigned long n = strtoul(opt_arg, &end_ptr, 10);
  289 + if (*end_ptr != '\0') {
  290 + LOG(ERROR, "Invalid integer argument: %s", opt_arg);
  291 + return false;
  292 + }
  293 + *opt.uint32_ptr = uint32_t(n);
  294 + break;
  295 + }
  296 + case kInt64: {
  297 + long long n = strtoll(opt_arg, &end_ptr, 10);
  298 + if (*end_ptr != '\0') {
  299 + LOG(ERROR, "Invalid integer argument: %s", opt_arg);
  300 + return false;
  301 + }
  302 + *opt.int64_ptr = int64_t(n);
  303 + break;
  304 + }
  305 + case kUInt64: {
  306 + unsigned long long n = strtoull(opt_arg, &end_ptr, 10);
  307 + if (*end_ptr != '\0') {
  308 + LOG(ERROR, "Invalid integer argument: %s", opt_arg);
  309 + return false;
  310 + }
  311 + *opt.uint64_ptr = uint64_t(n);
  312 + break;
  313 + }
  314 + case kDouble: {
  315 + double n = strtod(opt_arg, &end_ptr);
  316 + if (*end_ptr != '\0') {
  317 + LOG(ERROR, "Invalid double argument: %s", opt_arg);
  318 + return false;
  319 + }
  320 + *opt.double_ptr = n;
  321 + break;
  322 + }
  323 + case kString: {
  324 + *opt.string_ptr = opt_arg;
  325 + break;
  326 + }
  327 + case kIPv4: {
  328 + if (!parse_ipv4(opt_arg, opt.ipv4_ptr))
  329 + return false;
  330 + break;
  331 + }
  332 + case kMacAddr: {
  333 + if (!parse_mac_addr(opt_arg, opt.addr_ptr))
  334 + return false;
  335 + break;
  336 + }
  337 + default: assert(false); break;
  338 + }
  339 +
  340 + return true;
  341 +}
  342 +
  343 +bool opt_parser::parse_opts(int argc, char* const argv[]) {
  344 +// vector<char> options;
  345 +// options.reserve(short_args_.size() + 1);
  346 +//
  347 +// unordered_map<char, internal_opt>::const_iterator it;
  348 +// for (it = short_args_.begin(); it != short_args_.end(); ++it) {
  349 +// options.push_back(it->first);
  350 +// if (it->second.type != kBool)
  351 +// options.push_back(':');
  352 +// }
  353 +//
  354 +// options.push_back('\0');
  355 +
  356 + vector<option> long_opt;
  357 + long_opt.reserve(long_opts_.size() + 1);
  358 +
  359 + unordered_map<const char *, internal_opt>::const_iterator f;
  360 + for (f = long_opts_.begin(); f != long_opts_.end(); ++f) {
  361 + option arg = {f->first, f->second.optType == opt_argu ?
  362 + optional_argument : required_argument, 0, 0};
  363 + long_opt.push_back(arg);
  364 + }
  365 +
  366 + option end_indicator = {NULL, 0, NULL, 0};
  367 + long_opt.push_back(end_indicator);
  368 + int index, ret;
  369 +
  370 + // NOTE(liuyong): MUST initialize this variable first!
  371 + optind = 1;
  372 + optarg = NULL;
  373 +
  374 + while ((ret = getopt_long_only(argc, argv, "", &long_opt[0],
  375 + &index)) == 0 || (ret > 0 && ret != '?')) {
  376 + if (ret == 0) { // handle long options
  377 + const option &opt = long_opt[index];
  378 + const internal_opt &a = long_opts_[opt.name];
  379 + const char *arg = optarg;
  380 + if (!arg && optind < argc) {
  381 + arg = argv[optind];
  382 + ++optind;
  383 + }
  384 +
  385 + if (!fill_arg(opt.name, a, arg)) {
  386 + optind = 0;
  387 + return false;
  388 + }
  389 + }/* else if (short_args_.count(ret) > 0) { // handle short options
  390 + const internal_opt &a = short_args_[ret];
  391 + if (!fill_arg(a, optarg))
  392 + return false;
  393 + } */
  394 + }
  395 +
  396 + index = optind;
  397 + optind = 0;
  398 +
  399 +
  400 + if (index < argc) {
  401 + LOG(ERROR, "Unrecognized option argument: %s", argv[index]);
  402 + return false;
  403 + }
  404 +
  405 + if (ret != -1) {
  406 + LOG(ERROR, "Unregconized option argument %s", argv[index - 1]);
  407 + return false;
  408 + }
  409 +
  410 + return true;
  411 +}
  412 +
  413 +void opt_parser::clear() {
  414 +// short_args_.clear();
  415 + long_opts_.clear();
  416 +}
  417 +
  418 +void opt_parser::print_usage(const char *exec_file, ostream &sout) const {
  419 + sout << "Usage: \n " << exec_file << " ";
  420 + int fileLength = strlen(exec_file);
  421 + typedef unordered_map<const char*, internal_opt>::const_iterator It;
  422 + for (It f = long_opts_.begin(); f != long_opts_.end(); ++f) {
  423 + sout << "--" << f->first << " ";
  424 + const internal_opt &opt = f->second;
  425 + const char *opt_arg = NULL;
  426 +
  427 + switch (opt.type) {
  428 + case kBool: continue;
  429 + case kInt32:
  430 + opt_arg = "INTEGER32";
  431 + break;
  432 + case kUInt32:
  433 + opt_arg = "UINTEGER32";
  434 + break;
  435 + case kInt64:
  436 + opt_arg = "INTEGER64";
  437 + break;
  438 + case kUInt64:
  439 + opt_arg = "UINTEGER64";
  440 + break;
  441 + case kDouble:
  442 + opt_arg = "DOUBLE";
  443 + break;
  444 + case kString:
  445 + opt_arg = "STRING";
  446 + break;
  447 + case kIPv4:
  448 + opt_arg = "ddd.ddd.ddd.ddd";
  449 + break;
  450 + case kMacAddr:
  451 + opt_arg = "xx:xx:xx:xx:xx:xx";
  452 + break;
  453 + default:
  454 + assert(false);
  455 + break;
  456 + }
  457 +
  458 + sout << opt_arg << " ";
  459 + }
  460 +
  461 + sout << endl;
  462 +
  463 + for (It f = long_opts_.begin(); f != long_opts_.end(); ++f) {
  464 + sout<<" ";
  465 + for(int i = 0; i < fileLength; i++) sout<<" ";
  466 + sout << "--" << f->first << " ("<<f->second.help<<")"<<endl;
  467 + }
  468 +}
  469 +
  470 +}
  471 +}
  472 +
  1 +const callfile = require('child_process');
  2 +
  3 +
  4 +function shell(){
  5 +
  6 +}
  7 +
  8 +shell.prototype.Recording =async(appId,uid,channel)=>{
  9 + try {
  10 + let shell =`./RECORD_APP`+
  11 + ` --appId ` +appId
  12 + ` --uid ` +uid
  13 + ` --channel ` + channel
  14 + " --appliteDir \`pwd\`/../bin";
  15 +
  16 + let backShell =callfile.exec(shell);
  17 + return backShell;
  18 + } catch (error) {
  19 + throw error
  20 + }
  21 +
  22 +}
  23 +
  24 +
  25 +module.exports = new shell();
  1 +#!/usr/bin/env python
  2 +
  3 +import os
  4 +import sys
  5 +import glob
  6 +import subprocess
  7 +
  8 +HOME = os.path.dirname(os.path.realpath(__file__))
  9 +pathEnv=os.getenv('PATH')
  10 +os.environ['PATH']= "%s" %(HOME) + ":" + pathEnv
  11 +
  12 +
  13 +class AudioClip:
  14 + def __init__(self):
  15 + self.num = 0
  16 + self.filename = []
  17 + self.start_time = []
  18 + self.end_time = []
  19 +
  20 + def put_file(self, name):
  21 + if not (name in self.filename):
  22 + self.filename.append(name)
  23 + self.start_time.append(0.0)
  24 + self.end_time.append(0.0)
  25 + self.num = self.num + 1
  26 + return self.filename.index(name)
  27 +
  28 + def max_length(self):
  29 + return max(self.end_time)
  30 +
  31 + def print_filename(self):
  32 + str = ""
  33 + for i in range(self.num):
  34 + str = str + ("-i %s " % self.filename[i])
  35 + return str
  36 +
  37 + def print_filter(self):
  38 + str = ""
  39 + allch = ""
  40 + for i in range(self.num):
  41 + tmp = "[%d]adelay=%d[ad%d];" % ( (i), int(self.start_time[i]*1000)+1, (i))
  42 + allch = allch + ("[ad%d]" % i)
  43 + str = str + tmp
  44 + str = str + ("%s amix=inputs=%d:dropout_transition=0.5[audio]" % (allch, self.num))
  45 + return str
  46 +
  47 + def print_audio_info(self, i):
  48 + print "Audio Clip %d: %s: start_time=%.3f, end_time=%.3f" % (i, self.filename[i], self.start_time[i], self.end_time[i])
  49 +
  50 + def print_ffmpeg(self, output_file):
  51 + if self.num > 1:
  52 + str = "ffmpeg " + self.print_filename()
  53 + str = str + "-filter_complex \"%s\" " % self.print_filter()
  54 + str = str + "-map \"[audio]\" -to %f -y %s" % (self.max_length(), output_file)
  55 + elif self.num == 1:
  56 + str = "ffmpeg -i %s -c:a copy %s" % (self.filename[0], output_file)
  57 + else:
  58 + str = ""
  59 + return str
  60 +
  61 +class VideoClip:
  62 + def __init__(self):
  63 + self.num = 0
  64 + self.filename = []
  65 + self.start_time = []
  66 + self.end_time = []
  67 + self.width = []
  68 + self.height = []
  69 + self.audio_file = ""
  70 + self.audio_start_time = 0.0
  71 + self.audio_end_time = 0.0
  72 +
  73 + def put_file(self, name):
  74 + if not (name in self.filename):
  75 + self.filename.append(name)
  76 + self.start_time.append(0.0)
  77 + self.end_time.append(0.0)
  78 + self.width.append(0)
  79 + self.height.append(0)
  80 + self.num = self.num + 1
  81 + return self.filename.index(name)
  82 +
  83 + def max_resolution(self):
  84 + self.max_width = max(self.width)
  85 + self.max_height = max(self.height)
  86 + return self.max_width, self.max_height
  87 +
  88 + def max_length(self):
  89 + return max(max(self.end_time), self.audio_end_time)
  90 +
  91 + def audio_delay_needed(self):
  92 + return self.audio_file != "" and self.audio_start_time > 0.05
  93 +
  94 + def print_filter(self):
  95 + if self.audio_delay_needed():
  96 + audio_delay = int(self.audio_start_time*1000)
  97 + str = "[0]adelay=%d[audio];" % audio_delay
  98 + else:
  99 + str = ""
  100 + source = "1"
  101 + sink = "out2"
  102 + for i in range(self.num):
  103 + sink = "out%d" % (i+2)
  104 + if i == self.num - 1:
  105 + sink = "video"
  106 + tmp = "[%d]scale=%dx%d,setpts=PTS-STARTPTS+%.3f/TB[scale%d];[%s][scale%d]overlay=eof_action=pass[%s];" % \
  107 + ( (i+2), self.max_width, self.max_height, self.start_time[i], (i+2), source, (i+2), sink )
  108 + str = str + tmp
  109 + source = sink
  110 + return str[:-1]
  111 +
  112 + def print_filename(self):
  113 + str = ""
  114 + for i in range(self.num):
  115 + str = str + ("-i %s " % self.filename[i])
  116 + return str
  117 +
  118 + def print_ffmpeg(self, output_file):
  119 + if self.audio_file == "":
  120 + str = "ffmpeg -f lavfi -i anullsrc "
  121 + else:
  122 + str = "ffmpeg -i %s " % self.audio_file
  123 + str = str + "-f lavfi -i \"color=black:s=%dx%d:r=15\" " % (self.max_width, self.max_height)
  124 + str = str + self.print_filename()
  125 + str = str + "-filter_complex \"%s\" " % self.print_filter()
  126 + if self.audio_file == "":
  127 + map_option = "-map \"[video]\""
  128 + else:
  129 + if self.audio_delay_needed():
  130 + map_option = "-map \"[audio]\" -map \"[video]\" -c:a aac"
  131 + else:
  132 + map_option = "-map 0:a:0 -map \"[video]\" -c:a copy"
  133 + str = str + "%s -c:v libx264 -preset veryfast -to %f -y %s" % (map_option, self.max_length(), output_file)
  134 + return str
  135 +
  136 + def print_audio_info(self):
  137 + print "Audio Clip: %s: start_time=%.3f, end_time=%.3f" % (self.audio_file, self.audio_start_time, self.audio_end_time)
  138 +
  139 + def print_video_info(self, i):
  140 + print "Video Clip %d: %s: start_time=%.3f, end_time=%.3f, width=%d, height=%d" % \
  141 + (i, self.filename[i], self.start_time[i], self.end_time[i], self.width[i], self.height[i])
  142 +
  143 +
  144 +if len(sys.argv) <= 1:
  145 + print "Usage: python video_convert.py path_of_folder"
  146 + quit()
  147 +
  148 +folder_name = sys.argv[1]
  149 +print "Folder name:"+folder_name
  150 +
  151 +if not os.path.isdir(folder_name):
  152 + print "Folder "+folder_name+" does not exit"
  153 + quit()
  154 +
  155 +os.chdir(folder_name)
  156 +child_env = os.environ.copy()
  157 +all_uid_file = glob.glob("uid_*.txt")
  158 +
  159 +for uid_file in all_uid_file:
  160 + uid = os.path.splitext(uid_file)[0][4:]
  161 + print "UID:"+uid
  162 +
  163 + clip = VideoClip()
  164 + audio_clip = AudioClip()
  165 + with open(uid_file) as f:
  166 + for line in f:
  167 + items = line.split(" ")
  168 + #audio file
  169 + if items[1][-3:] == "aac":
  170 + index = audio_clip.put_file(items[1])
  171 + if items[2] == "create":
  172 + audio_clip.start_time[index] = float(items[0])
  173 + elif items[2] == "close":
  174 + audio_clip.end_time[index] = float(items[0])
  175 + #video file
  176 + if items[1][-3:] == "mp4":
  177 + index = clip.put_file(items[1])
  178 + if items[2] == "create":
  179 + clip.start_time[index] = float(items[0])
  180 + elif items[2] == "info":
  181 + clip.start_time[index] = float(items[0])
  182 + clip.width[index] = int(items[3][6:])
  183 + clip.height[index] = int(items[4][7:])
  184 + rotation = int(items[5][9:])
  185 + if rotation == 90 or rotation == 270:
  186 + clip.width[index], clip.height[index] = clip.height[index], clip.width[index]
  187 + elif items[2] == "close":
  188 + clip.end_time[index] = float(items[0])
  189 + #video file
  190 + if items[1][-4:] == "webm":
  191 + index = clip.put_file(items[1])
  192 + if items[2] == "create":
  193 + clip.start_time[index] = float(items[0])
  194 + elif items[2] == "info":
  195 + clip.start_time[index] = float(items[0])
  196 + clip.width[index] = int(items[3][6:])
  197 + clip.height[index] = int(items[4][7:])
  198 + rotation = int(items[5][9:])
  199 + if rotation == 90 or rotation == 270:
  200 + clip.width[index], clip.height[index] = clip.height[index], clip.width[index]
  201 + elif items[2] == "close":
  202 + clip.end_time[index] = float(items[0])
  203 +
  204 +
  205 + clip.print_audio_info()
  206 + for i in range(audio_clip.num):
  207 + audio_clip.print_audio_info(i)
  208 + for i in range(clip.num):
  209 + clip.print_video_info(i)
  210 +
  211 + if audio_clip.num > 1:
  212 + print "Generate Audio File"
  213 + tmp_audio = uid+"_tmp.m4a"
  214 + command = audio_clip.print_ffmpeg(tmp_audio)
  215 + clip.audio_file = tmp_audio
  216 + clip.audio_start_time = 0.0
  217 + clip.audio_end_time = audio_clip.max_length()
  218 + print command
  219 + print subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, env=child_env).stdout.read()
  220 + elif audio_clip.num == 1:
  221 + clip.audio_file = audio_clip.filename[0]
  222 + clip.audio_start_time = audio_clip.start_time[0]
  223 + clip.audio_end_time = audio_clip.end_time[0]
  224 +
  225 + if clip.num > 0:
  226 + print "Generate MP4 file:"
  227 + print "Output resolution:", clip.max_resolution()
  228 + output_file = uid+"_av"+".mp4"
  229 + #print clip.print_filter()
  230 + command = clip.print_ffmpeg(output_file)
  231 + else:
  232 + tmp_audio = uid+"_tmp.m4a"
  233 + output_file = uid+".m4a"
  234 + if audio_clip.num > 1:
  235 + command = "mv %s %s" % (tmp_audio, output_file)
  236 + elif audio_clip.num == 1:
  237 + command = "ffmpeg -i %s -c:a copy -y %s" % (clip.audio_file, output_file)
  238 + print command
  239 + print subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, env=child_env).stdout.read()
  240 + print "\n\n"
  241 +
  242 +#write a convert done file
  243 +f = open("convert-done.txt", "w+")
  244 +f.close()
  245 +
  246 +#remove tmp files
  247 +os.system('rm -f *_tmp.m4a')
  248 +
@@ -20,10 +20,6 @@ var hexDecode = function(str) { @@ -20,10 +20,6 @@ var hexDecode = function(str) {
20 return new Buffer(str, 'hex'); 20 return new Buffer(str, 'hex');
21 }; 21 };
22 22
23 -  
24 -  
25 -  
26 -  
27 var Message = function(options) { 23 var Message = function(options) {
28 options.pack = function() { 24 options.pack = function() {
29 var out = ByteBuf(); 25 var out = ByteBuf();
@@ -52,10 +48,10 @@ var IN_CHANNEL_PERMISSION = 4; @@ -52,10 +48,10 @@ var IN_CHANNEL_PERMISSION = 4;
52 48
53 var inspectMediaChannelKey = function async(appID, appCertificate, channelName, unixTs, randomInt, uid, expiredTs) { 49 var inspectMediaChannelKey = function async(appID, appCertificate, channelName, unixTs, randomInt, uid, expiredTs) {
54 var rawAppID = hexDecode(appID); 50 var rawAppID = hexDecode(appID);
55 - console.log('App ID:\t\t\t ' + rawAppID.toString('hex').toUpperCase()); 51 + // console.log('App ID:\t\t\t ' + rawAppID.toString('hex').toUpperCase());
56 52
57 var rawAppCertificate = hexDecode(appCertificate); 53 var rawAppCertificate = hexDecode(appCertificate);
58 - console.log('App Certificate:\t ' + rawAppCertificate.toString('hex').toUpperCase()); 54 + // console.log('App Certificate:\t ' + rawAppCertificate.toString('hex').toUpperCase());
59 55
60 var serviceType = MEDIA_CHANNEL_SERVICE; 56 var serviceType = MEDIA_CHANNEL_SERVICE;
61 var extra = null; 57 var extra = null;
@@ -120,7 +116,6 @@ var inspectMediaChannelKey = function async(appID, appCertificate, channelName, @@ -120,7 +116,6 @@ var inspectMediaChannelKey = function async(appID, appCertificate, channelName,
120 return that; 116 return that;
121 } 117 }
122 var Message = function(options) { 118 var Message = function(options) {
123 - console.log('s生成buffer')  
124 119
125 options.pack = function() { 120 options.pack = function() {
126 var out = ByteBuf(); 121 var out = ByteBuf();
@@ -150,10 +145,10 @@ var inspectMediaChannelKey = function async(appID, appCertificate, channelName, @@ -150,10 +145,10 @@ var inspectMediaChannelKey = function async(appID, appCertificate, channelName,
150 }); 145 });
151 146
152 var toSign = m.pack(); 147 var toSign = m.pack();
153 - console.log("Message to sign:\t " + toSign.toString('hex').toUpperCase()); 148 + // console.log("Message to sign:\t " + toSign.toString('hex').toUpperCase());
154 149
155 var signature = encodeHMac(rawAppCertificate, toSign); 150 var signature = encodeHMac(rawAppCertificate, toSign);
156 - console.log("Signature:\t\t " + signature.toString('hex').toUpperCase()); 151 + // console.log("Signature:\t\t " + signature.toString('hex').toUpperCase());
157 var DynamicKey5Content = function(options) { 152 var DynamicKey5Content = function(options) {
158 options.pack = function() { 153 options.pack = function() {
159 var out = ByteBuf(); 154 var out = ByteBuf();
@@ -177,10 +172,10 @@ var inspectMediaChannelKey = function async(appID, appCertificate, channelName, @@ -177,10 +172,10 @@ var inspectMediaChannelKey = function async(appID, appCertificate, channelName,
177 , salt: randomInt 172 , salt: randomInt
178 , expiredTs: expiredTs 173 , expiredTs: expiredTs
179 , extra: extra}).pack(); 174 , extra: extra}).pack();
180 - console.log("Content to encode:\t " + content.toString('hex').toUpperCase()); 175 + // console.log("Content to encode:\t " + content.toString('hex').toUpperCase());
181 176
182 var channelKey = version + content.toString('base64'); 177 var channelKey = version + content.toString('base64');
183 - console.log("Channel key:\t\t " + channelKey); 178 + // console.log("Channel key:\t\t " + channelKey);
184 return channelKey; 179 return channelKey;
185 }; 180 };
186 181