video analysis2.0拆分,ffmpeg封装go接口库
chenshijun
2019-09-10 a6dd7933e0bd8ae1fd083639758f7fee9fc7a151
Merge branch 'master' of ssh://192.168.1.14:29418/valib/goffmpeg
1个文件已添加
10个文件已修改
1228 ■■■■■ 已修改文件
cffmpeg.h 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/buz/recorder.cpp 85 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/buz/recorder.hpp 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/buz/sole.hpp 902 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/cffmpeg.cpp 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/ffmpeg/format/FormatOut.cpp 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/wrapper.cpp 170 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
csrc/wrapper.hpp 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
goffmpeg.go 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
libcffmpeg.c 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
libcffmpeg.h 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cffmpeg.h
@@ -23,7 +23,7 @@
char* c_ffmpeg_get_rec_id(const cffmpeg h, const char* p, int *length);
void c_ffmpeg_build_decoder(const cffmpeg h);
void* c_ffmpeg_get_pic_decoder(const cffmpeg h, int *wid, int *hei);
void* c_ffmpeg_get_pic_decoder(const cffmpeg h, int *wid, int *hei, int64_t *id);
void* c_ffmpeg_get_avpacket(const cffmpeg h, int *size, int *key);
//////test
csrc/buz/recorder.cpp
@@ -1,7 +1,9 @@
#include "recorder.hpp"
#include "sole.hpp"
#include <thread>
#include <unistd.h>
#include <chrono>
extern "C"{
#include <libavcodec/avcodec.h>
@@ -30,11 +32,31 @@
        ,file_frame_index_(-1)
        ,file_path_("")
        ,func_rec_info_(nullptr)
        {}
        ,thrd_(nullptr)
        ,error_occured_(false)
        {
            // logIt("RECODER ID: %s", id_.c_str());
        }
        Recorder::~Recorder(){
            stop_recorder_.store(true);
            cv_.notify_one();
            try
            {
                if (thrd_){
                    {
                        std::unique_lock<std::mutex> locker(mutex_pkt_);
                        stop_recorder_.store(true);
                        cv_.notify_one();
                    }
                    thrd_->join();
                    logIt("REC THREAD JOINED, QUIT!!!");
                }
            }
            catch(const std::exception& e)
            {
                logIt("RECODER DESTRUCTOR EXCEPTION: ", e.what());
            }
        }
        int Recorder::init_writer(){
@@ -54,10 +76,11 @@
        void Recorder::start_writer(){
            if (cur_frame == 0) {
                srandom(time(NULL));
                file_path_ = dir_ + "/" + std::to_string(random()) + ".mp4";
                sole::uuid u4 = sole::uuid4();
                file_path_ = dir_ + "/" + u4.base62() + ".mp4";
                out_->JustWriter(in_->getStream(), file_path_.c_str());
                logIt("start record %s", file_path_.c_str());
                logIt("START RECORD %s", file_path_.c_str());
            }
        }
@@ -69,19 +92,28 @@
            }
            // writer error, reinit writer
            int64_t cur = cur_frame++;
            if(!out_->writeFrame(pkt.data->getAVPacket(), cur)){
            AVPacket &op = pkt.data->getAVPacket();
            AVPacket np(op);
            av_copy_packet(&np, &op);
            if(!out_->writeFrame(np, cur)){
                av_packet_unref(&np);
                end_writer();
                return -1;
            }
            av_packet_unref(&np);
            if(pkt.id == id_frame_){
                file_frame_index_ = cur_frame;
                file_frame_index_ = cur_frame-1;
            }
            // logIt("WRITE FRAME ID: %d, RECORD ID: %d", pkt.id, id_frame_);
            return 0;
        }
        void Recorder::end_writer(){
            if(cur_frame == -1) return;
            out_->endWriter();
            logIt("INDEX %d, REAL-FRAME-ID %d, FILE %s, CURFrame %d, ENDFrame %d\n",
                 file_frame_index_, id_frame_, file_path_.c_str(), cur_frame, end_frame);
            //reinit cur_frame clear list pkt
            {
                std::lock_guard<std::mutex> locker(mutex_pkt_);
@@ -89,12 +121,10 @@
                end_frame = minduration;
                list_pkt_.clear();
            }
            //callback to frame index and path
            if(func_rec_info_){
                func_rec_info_(id_,file_frame_index_, file_path_);
                // logIt("recoder index %d, file name %s\n", file_frame_index_, file_path_.c_str());
            }else{
                // logIt("recorder has no func_rec_info");
            }
        }
@@ -113,9 +143,15 @@
                std::list<avpacket> pkts;
                {
                    std::unique_lock<std::mutex> locker(mutex_pkt_);
                    cv_.wait(locker,[&]{
                    auto status = cv_.wait_for(locker, std::chrono::seconds(10), [&]{
                        return !list_pkt_.empty() || stop_recorder_.load();
                    });
                    if (!status){
                        end_writer();
                        error_occured_ = true;
                        break;
                    }
                    if(stop_recorder_.load()){
                        end_writer();
                        break;
@@ -132,21 +168,24 @@
                for(auto &i : pkts){
                    if (cur_frame < end_frame){
                        const int ret = write_correctly(i);
                        if(ret != 0){
                            if(ret == -1) reinit_writer = true;
                        if(write_correctly(i) != 0){
                            stop_recorder_.store(true);
                            break;
                        }
                    }else{
                        end_writer();
                        stop_recorder_.store(true);
                        break;
                    }
                }
            }
            if (out_){
                delete out_;
                out_ = NULL;
            }
            // stop_recorder_.store(false);
        }
        int Recorder::Run(const char* output, const int mind, const int maxd){
@@ -165,11 +204,12 @@
                end_frame = minduration;
            }
            logIt("min %d max %d endcount %d", minduration, maxduration, end_frame);
            logIt("minduration %d maxduration %d curduration %d", minduration, maxduration, end_frame);
            std::thread([&]{
            thrd_.reset(new std::thread([&]{
                run_thread();
            }).detach();
            }));
            //.detach();
            return 0;
        }
@@ -177,9 +217,15 @@
        int Recorder::FireRecorder(const int64_t &id){
            if(cur_frame == -1){
                id_frame_ = id;
                logIt("FIRST FIRE RECORD ID: %lld", id);
                {
                    std::lock_guard<std::mutex> locker(mutex_pkt_);
                    cur_frame = 0;
                    if (list_pkt_.size() > end_frame){
                        end_frame = list_pkt_.size() + minduration/2;
                        if (end_frame > maxduration)
                            end_frame = maxduration;
                    }
                }
            }else if(end_frame - cur_frame > minduration/2 && end_frame < maxduration){
                end_frame = end_frame + minduration / 2;
@@ -187,6 +233,7 @@
                    end_frame = maxduration;
                }
            }
            // logIt("FIRE REC FRAME ID: %lld", id);
            return 0;
        }
@@ -235,4 +282,4 @@
            }
        }
    }
}
}
csrc/buz/recorder.hpp
@@ -40,6 +40,8 @@
                void SetCallback(FUNC_REC_INFO cb){
                    func_rec_info_ = cb;
                }
                const bool ErrorOcurred(){return error_occured_;}
            private:
                void run_thread();
@@ -64,6 +66,8 @@
                std::mutex              mutex_pkt_;
                 std::condition_variable cv_;
                std::unique_ptr<std::thread> thrd_;
                std::string             dir_;
                std::string             id_;
@@ -71,6 +75,8 @@
                int                     file_frame_index_;
                std::string             file_path_;
                FUNC_REC_INFO           func_rec_info_;
                bool                    error_occured_;
        };
    }
}
csrc/buz/sole.hpp
New file
@@ -0,0 +1,902 @@
/* Sole is a lightweight C++11 library to generate universally unique identificators.
 * Sole provides interface for UUID versions 0, 1 and 4.
 * https://github.com/r-lyeh/sole
 * Copyright (c) 2013,2014,2015 r-lyeh. zlib/libpng licensed.
 * Based on code by Dmitri Bouianov, Philip O'Toole, Poco C++ libraries and anonymous
 * code found on the net. Thanks guys!
 * Theory: (see Hoylen's answer at [1])
 * - UUID version 1 (48-bit MAC address + 60-bit clock with a resolution of 100ns)
 *   Clock wraps in 3603 A.D.
 *   Up to 10000000 UUIDs per second.
 *   MAC address revealed.
 *
 * - UUID Version 4 (122-bits of randomness)
 *   See [2] or other analysis that describe how very unlikely a duplicate is.
 *
 * - Use v1 if you need to sort or classify UUIDs per machine.
 *   Use v1 if you are worried about leaving it up to probabilities (e.g. your are the
 *   type of person worried about the earth getting destroyed by a large asteroid in your
 *   lifetime). Just use a v1 and it is guaranteed to be unique till 3603 AD.
 *
 * - Use v4 if you are worried about security issues and determinism. That is because
 *   v1 UUIDs reveal the MAC address of the machine it was generated on and they can be
 *   predictable. Use v4 if you need more than 10 million uuids per second, or if your
 *   application wants to live past 3603 A.D.
 * Additionally a custom UUID v0 is provided:
 * - 16-bit PID + 48-bit MAC address + 60-bit clock with a resolution of 100ns since Unix epoch
 * - Format is EPOCH_LOW-EPOCH_MID-VERSION(0)|EPOCH_HI-PID-MAC
 * - Clock wraps in 3991 A.D.
 * - Up to 10000000 UUIDs per second.
 * - MAC address and PID revealed.
 * References:
 * - [1] http://stackoverflow.com/questions/1155008/how-unique-is-uuid
 * - [2] http://en.wikipedia.org/wiki/UUID#Random%5FUUID%5Fprobability%5Fof%5Fduplicates
 * - http://en.wikipedia.org/wiki/Universally_unique_identifier
 * - http://en.cppreference.com/w/cpp/numeric/random/random_device
 * - http://www.itu.int/ITU-T/asn1/uuid.html f81d4fae-7dec-11d0-a765-00a0c91e6bf6
 * - rlyeh ~~ listening to Hedon Cries / Until The Sun Goes up
 */
//////////////////////////////////////////////////////////////////////////////////////
#pragma once
#include <stdint.h>
#include <stdio.h>     // for size_t; should be stddef.h instead; however, clang+archlinux fails when compiling it (@Travis-Ci)
#include <sys/types.h> // for uint32_t; should be stdint.h instead; however, GCC 5 on OSX fails when compiling it (See issue #11)
#include <functional>
#include <string>
// public API
#define SOLE_VERSION "1.0.1" /* (2017/05/16): Improve UUID4 and base62 performance; fix warnings
#define SOLE_VERSION "1.0.0" // (2016/02/03): Initial semver adherence; Switch to header-only; Remove warnings */
namespace sole
{
    // 128-bit basic UUID type that allows comparison and sorting.
    // Use .str() for printing and .pretty() for pretty printing.
    // Also, ostream friendly.
    struct uuid
    {
        uint64_t ab;
        uint64_t cd;
        bool operator==( const uuid &other ) const;
        bool operator!=( const uuid &other ) const;
        bool operator <( const uuid &other ) const;
        std::string pretty() const;
        std::string base62() const;
        std::string str() const;
        template<typename ostream>
        inline friend ostream &operator<<( ostream &os, const uuid &self ) {
            return os << self.str(), os;
        }
    };
    // Generators
    uuid uuid0(); // UUID v0, pro: unique; cons: MAC revealed, pid revealed, predictable.
    uuid uuid1(); // UUID v1, pro: unique; cons: MAC revealed, predictable.
    uuid uuid4(); // UUID v4, pros: anonymous, fast; con: uuids "can clash"
    // Rebuilders
    uuid rebuild( uint64_t ab, uint64_t cd );
    uuid rebuild( const std::string &uustr );
}
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4127)
#endif
namespace std {
    template<>
    struct hash< sole::uuid > {
    public:
        // hash functor: hash uuid to size_t value by pseudorandomizing transform
        size_t operator()( const sole::uuid &uuid ) const {
            if( sizeof(size_t) > 4 ) {
                return size_t( uuid.ab ^ uuid.cd );
            } else {
                uint64_t hash64 = uuid.ab ^ uuid.cd;
                return size_t( uint32_t( hash64 >> 32 ) ^ uint32_t( hash64 ) );
            }
        }
    };
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
// implementation
#include <memory.h>
#include <stdint.h>
#include <stdio.h>
#include <time.h>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <random>
#include <sstream>
#include <string>
#include <vector>
#if defined(_WIN32)
#   include <winsock2.h>
#   include <process.h>
#   include <iphlpapi.h>
#   ifdef _MSC_VER
#   pragma comment(lib,"iphlpapi.lib")
#   endif
#   define $windows $yes
#elif defined(__FreeBSD__) || defined(__NetBSD__) || \
        defined(__OpenBSD__) || defined(__MINT__) || defined(__bsdi__)
#   include <ifaddrs.h>
#   include <net/if_dl.h>
#   include <sys/socket.h>
#   include <sys/time.h>
#   include <sys/types.h>
#   include <unistd.h>
#   define $bsd $yes
#elif (defined(__APPLE__) && defined(__MACH__))
#   include <ifaddrs.h>
#   include <net/if_dl.h>
#   include <sys/socket.h>
#   include <sys/time.h>
#   include <sys/types.h>
#   include <unistd.h>
#   pragma clang diagnostic push
#   pragma clang diagnostic ignored "-Wdollar-in-identifier-extension"
#   define $osx $yes
#elif defined(__linux__)
#   include <arpa/inet.h>
#   include <net/if.h>
#   include <netinet/in.h>
#   include <sys/ioctl.h>
#   include <sys/socket.h>
#   include <sys/time.h>
#   include <unistd.h>
#   define $linux $yes
#else //elif defined(__unix__)
#   if defined(__VMS)
#      include <ioctl.h>
#      include <inet.h>
#   else
#      include <sys/ioctl.h>
#      include <arpa/inet.h>
#   endif
#   if defined(sun) || defined(__sun)
#      include <sys/sockio.h>
#   endif
#   include <net/if.h>
#   include <net/if_arp.h>
#   include <netdb.h>
#   include <netinet/in.h>
#   include <sys/socket.h>
#   include <sys/time.h>
#   include <sys/types.h>
#   include <unistd.h>
#   if defined(__VMS)
        namespace { enum { MAXHOSTNAMELEN = 64 }; }
#   endif
#   define $unix $yes
#endif
#ifdef _MSC_VER
#   define $msvc  $yes
#endif
#if defined(__GNUC__) && (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 < 50100)
    namespace std
    {
        static inline std::string put_time( const std::tm* tmb, const char* fmt ) {
            std::string s( 128, '\0' );
            while( !strftime( &s[0], s.size(), fmt, tmb ) )
                s.resize( s.size() + 128 );
            return s;
        }
    }
#endif
////////////////////////////////////////////////////////////////////////////////////
#ifdef  $windows
#define $welse   $no
#else
#define $windows $no
#define $welse   $yes
#endif
#ifdef  $bsd
#define $belse   $no
#else
#define $bsd     $no
#define $belse   $yes
#endif
#ifdef  $linux
#define $lelse   $no
#else
#define $linux   $no
#define $lelse   $yes
#endif
#ifdef  $unix
#define $uelse   $no
#else
#define $unix    $no
#define $uelse   $yes
#endif
#ifdef  $osx
#define $oelse   $no
#else
#define $osx     $no
#define $oelse   $yes
#endif
#ifdef  $msvc
#define $melse   $no
#else
#define $msvc    $no
#define $melse   $yes
#endif
#define $yes(...) __VA_ARGS__
#define $no(...)
inline bool sole::uuid::operator==( const sole::uuid &other ) const {
    return ab == other.ab && cd == other.cd;
}
inline bool sole::uuid::operator!=( const sole::uuid &other ) const {
    return !operator==(other);
}
inline bool sole::uuid::operator<( const sole::uuid &other ) const {
    if( ab < other.ab ) return true;
    if( ab > other.ab ) return false;
    if( cd < other.cd ) return true;
    return false;
}
namespace sole {
    inline std::string printftime( uint64_t timestamp_secs = 0, const std::string &locale = std::string() ) {
        std::string timef;
        try {
            // Taken from parameter
            //std::string locale; // = "es-ES", "Chinese_China.936", "en_US.UTF8", etc...
            std::time_t t = timestamp_secs;
            std::tm tm;
            $msvc(
                localtime_s( &tm, &t );
            )
            $melse(
                $windows(tm = *localtime( &t ); )
                $welse( localtime_r(&t, &tm); )
            )
            std::stringstream ss;
            $melse(
            std::locale lc( locale.c_str() );
            ss.imbue( lc );
            )
            ss << std::put_time( &tm, "\"%c\"" );
            timef = ss.str();
        }
        catch(...) {
            timef = "\"\"";
        }
        return timef;
    }
    inline std::string uuid::pretty() const {
        std::stringstream ss;
        uint64_t a = (ab >> 32);
        uint64_t b = (ab & 0xFFFFFFFF);
        uint64_t c = (cd >> 32);
        uint64_t d = (cd & 0xFFFFFFFF);
        int version = (b & 0xF000) >> 12;
        uint64_t timestamp = ((b & 0x0FFF) << 48 ) | (( b >> 16 ) << 32) | a; // in 100ns units
        ss << "version=" << (version) << ',';
        if( version == 1 )
            timestamp = timestamp - 0x01b21dd213814000ULL; // decrement Gregorian calendar
        ss << std::hex << std::nouppercase << std::setfill('0');
        version <= 1 && ss << "timestamp=" << printftime(timestamp/10000000) << ',';
        version <= 1 && ss << "mac=" << std::setw(4) << (c & 0xFFFF) << std::setw(8) << d << ',';
        version == 4 && ss << "randbits=" << std::setw(8) << (ab & 0xFFFFFFFFFFFF0FFFULL) << std::setw(8) << (cd & 0x3FFFFFFFFFFFFFFFULL) << ',';
        ss << std::dec;
        version == 0 && ss << "pid=" << std::setw(4) << (c >> 16 ) << ',';
        version == 1 && ss << "clock_seq=" << std::setw(4) << ((c >> 16) & 0x3FFF) << ',';
        return ss.str();
    }
    inline std::string uuid::str() const {
        std::stringstream ss;
        ss << std::hex << std::nouppercase << std::setfill('0');
        uint32_t a = (ab >> 32);
        uint32_t b = (ab & 0xFFFFFFFF);
        uint32_t c = (cd >> 32);
        uint32_t d = (cd & 0xFFFFFFFF);
        ss << std::setw(8) << (a) << '-';
        ss << std::setw(4) << (b >> 16) << '-';
        ss << std::setw(4) << (b & 0xFFFF) << '-';
        ss << std::setw(4) << (c >> 16 ) << '-';
        ss << std::setw(4) << (c & 0xFFFF);
        ss << std::setw(8) << d;
        return ss.str();
    }
    inline std::string uuid::base62() const {
        int base62len = 10 + 26 + 26;
        const char base62[] =
            "0123456789"
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            "abcdefghijklmnopqrstuvwxyz";
        char res[24], *end = &res[24]; *(--end) = '\0';
        uint64_t rem, AB = ab, CD = cd;
        do {
            rem = CD % base62len;
            *--end = base62[int(rem)];
            CD /= base62len;
        } while (CD > 0);
        *--end = '-';
        do {
            rem = AB % base62len;
            *--end = base62[int(rem)];
            AB /= base62len;
        } while (AB > 0);
        return end;
    }
    //////////////////////////////////////////////////////////////////////////////////////
    // multiplatform clock_gettime()
    $windows(
        struct timespec {
            uint64_t tv_sec;
            uint64_t tv_nsec;
        };
        struct timezone {
            int  tz_minuteswest; /* minutes W of Greenwich */
            int  tz_dsttime;     /* type of dst correction */
        };
        inline int gettimeofday( struct timeval *tv, struct timezone *tz ) {
            FILETIME ft;
            uint64_t tmpres = 0;
            if( NULL != tv ) {
                GetSystemTimeAsFileTime(&ft);
                // The GetSystemTimeAsFileTime returns the number of 100 nanosecond
                // intervals since Jan 1, 1601 in a structure. Copy the high bits to
                // the 64 bit tmpres, shift it left by 32 then or in the low 32 bits.
                tmpres |= ft.dwHighDateTime;
                tmpres <<= 32;
                tmpres |= ft.dwLowDateTime;
                // Convert to microseconds by dividing by 10
                tmpres /= 10;
                // The Unix epoch starts on Jan 1 1970.  Need to subtract the difference
                // in seconds from Jan 1 1601.
                tmpres -= 11644473600000000ULL;
                // Finally change microseconds to seconds and place in the seconds value.
                // The modulus picks up the microseconds.
                tv->tv_sec = static_cast<long>(tmpres / 1000000UL);
                tv->tv_usec = (tmpres % 1000000UL);
            }
            if( NULL != tz ) {
                static bool once = true;
                if( once ) {
                    once = false;
                    _tzset();
                }
                long timezoneSecs = 0;
                int daylight = 0;
                $msvc(
                    _get_timezone(&timezoneSecs);
                    _get_daylight(&daylight);
                )
                $melse(
                    timezoneSecs = _timezone;
                    daylight = _daylight;
                )
                tz->tz_minuteswest = timezoneSecs / 60;
                tz->tz_dsttime = daylight;
            }
            return 0;
        }
    )
    $lelse( $belse( // if not linux, if not bsd... valid for apple/win32
        inline int clock_gettime( int /*clk_id*/, struct timespec* t ) {
            struct timeval now;
            int rv = gettimeofday(&now, NULL);
            if( rv ) return rv;
            t->tv_sec  = now.tv_sec;
            t->tv_nsec = now.tv_usec * 1000;
            return 0;
        }
    ))
    //////////////////////////////////////////////////////////////////////////////////////
    // Timestamp and MAC interfaces
    // Returns number of 100ns intervals
    inline uint64_t get_time( uint64_t offset ) {
        struct timespec tp;
        clock_gettime(0 /*CLOCK_REALTIME*/, &tp);
        // Convert to 100-nanosecond intervals
        uint64_t uuid_time;
        uuid_time = tp.tv_sec * 10000000;
        uuid_time = uuid_time + (tp.tv_nsec / 100);
        uuid_time = uuid_time + offset;
        // If the clock looks like it went backwards, or is the same, increment it.
        static uint64_t last_uuid_time = 0;
        if( last_uuid_time > uuid_time )
            last_uuid_time = uuid_time;
        else
            last_uuid_time = ++uuid_time;
        return uuid_time;
    }
    // Looks for first MAC address of any network device, any size.
    inline bool get_any_mac( std::vector<unsigned char> &_node ) {
    $windows({
        PIP_ADAPTER_INFO pAdapterInfo;
        PIP_ADAPTER_INFO pAdapter = 0;
        ULONG len    = sizeof(IP_ADAPTER_INFO);
        pAdapterInfo = reinterpret_cast<IP_ADAPTER_INFO*>(new char[len]);
        // Make an initial call to GetAdaptersInfo to get
        // the necessary size into len
        DWORD rc = GetAdaptersInfo(pAdapterInfo, &len);
        if (rc == ERROR_BUFFER_OVERFLOW)
        {
            delete [] reinterpret_cast<char*>(pAdapterInfo);
            pAdapterInfo = reinterpret_cast<IP_ADAPTER_INFO*>(new char[len]);
        }
        else if (rc != ERROR_SUCCESS)
        {
            return $no("cannot get network adapter list") false;
        }
        bool found = false, gotten = false;
        if (GetAdaptersInfo(pAdapterInfo, &len) == NO_ERROR)
        {
            gotten = true;
            pAdapter = pAdapterInfo;
            while (pAdapter && !found)
            {
                if (pAdapter->Type == MIB_IF_TYPE_ETHERNET && pAdapter->AddressLength > 0 )
                {
                    _node.resize( pAdapter->AddressLength );
                    std::memcpy(_node.data(), pAdapter->Address, _node.size() );
                    found = true;
                }
                pAdapter = pAdapter->Next;
            }
        }
        delete [] reinterpret_cast<char*>(pAdapterInfo);
        if( !gotten )
            return $no("cannot get network adapter list") false;
        if (!found)
            return $no("no Ethernet adapter found") false;
        return true;
    })
    $bsd({
        struct ifaddrs* ifaphead;
        int rc = getifaddrs(&ifaphead);
        if (rc) return $no("cannot get network adapter list") false;
        bool foundAdapter = false;
        for (struct ifaddrs* ifap = ifaphead; ifap; ifap = ifap->ifa_next)
        {
            if (ifap->ifa_addr && ifap->ifa_addr->sa_family == AF_LINK)
            {
                struct sockaddr_dl* sdl = reinterpret_cast<struct sockaddr_dl*>(ifap->ifa_addr);
                caddr_t ap = (caddr_t) (sdl->sdl_data + sdl->sdl_nlen);
                int alen = sdl->sdl_alen;
                if (ap && alen > 0)
                {
                    _node.resize( alen );
                    std::memcpy(_node.data(), ap, _node.size() );
                    foundAdapter = true;
                    break;
                }
            }
        }
        freeifaddrs(ifaphead);
        if (!foundAdapter) return $no("cannot determine MAC address (no suitable network adapter found)") false;
        return true;
    })
    $osx({
        struct ifaddrs* ifaphead;
        int rc = getifaddrs(&ifaphead);
        if (rc) return $no("cannot get network adapter list") false;
        bool foundAdapter = false;
        for (struct ifaddrs* ifap = ifaphead; ifap; ifap = ifap->ifa_next)
        {
            if (ifap->ifa_addr && ifap->ifa_addr->sa_family == AF_LINK)
            {
                struct sockaddr_dl* sdl = reinterpret_cast<struct sockaddr_dl*>(ifap->ifa_addr);
                caddr_t ap = (caddr_t) (sdl->sdl_data + sdl->sdl_nlen);
                int alen = sdl->sdl_alen;
                if (ap && alen > 0)
                {
                    _node.resize( alen );
                    std::memcpy(_node.data(), ap, _node.size() );
                    foundAdapter = true;
                    break;
                }
            }
        }
        freeifaddrs(ifaphead);
        if (!foundAdapter) return $no("cannot determine MAC address (no suitable network adapter found)") false;
        return true;
    })
    $linux({
        struct ifreq ifr;
        int s = socket(PF_INET, SOCK_DGRAM, 0);
        if (s == -1) return $no("cannot open socket") false;
        std::strcpy(ifr.ifr_name, "eth0");
        int rc = ioctl(s, SIOCGIFHWADDR, &ifr);
        close(s);
        if (rc < 0) return $no("cannot get MAC address") false;
        struct sockaddr* sa = reinterpret_cast<struct sockaddr*>(&ifr.ifr_addr);
        _node.resize( sizeof(sa->sa_data) );
        std::memcpy(_node.data(), sa->sa_data, _node.size() );
        return true;
    })
    $unix({
        char name[MAXHOSTNAMELEN];
        if (gethostname(name, sizeof(name)))
            return $no("cannot get host name") false;
        struct hostent* pHost = gethostbyname(name);
        if (!pHost) return $no("cannot get host IP address") false;
        int s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
        if (s == -1) return $no("cannot open socket") false;
        struct arpreq ar;
        std::memset(&ar, 0, sizeof(ar));
        struct sockaddr_in* pAddr = reinterpret_cast<struct sockaddr_in*>(&ar.arp_pa);
        pAddr->sin_family = AF_INET;
        std::memcpy(&pAddr->sin_addr, *pHost->h_addr_list, sizeof(struct in_addr));
        int rc = ioctl(s, SIOCGARP, &ar);
        close(s);
        if (rc < 0) return $no("cannot get MAC address") false;
        _node.resize( sizeof(ar.arp_ha.sa_data) );
        std::memcpy(_node.data(), ar.arp_ha.sa_data, _node.size());
        return true;
    })
    }
    // Looks for first MAC address of any network device, size truncated to 48bits.
    inline uint64_t get_any_mac48() {
        std::vector<unsigned char> node;
        if( get_any_mac(node) ) {
            std::stringstream ss;
            ss << std::hex << std::setfill('0');
            node.resize(6);
            for( unsigned i = 0; i < 6; ++i )
                ss << std::setw(2) << int(node[i]);
            uint64_t t;
            if( ss >> t )
                return t;
        }
        return 0;
    }
    //////////////////////////////////////////////////////////////////////////////////////
    // UUID implementations
    inline uuid uuid4() {
        static std::random_device rd;
        static std::uniform_int_distribution<uint64_t> dist(0, (uint64_t)(~0));
        uuid my;
        my.ab = dist(rd);
        my.cd = dist(rd);
        my.ab = (my.ab & 0xFFFFFFFFFFFF0FFFULL) | 0x0000000000004000ULL;
        my.cd = (my.cd & 0x3FFFFFFFFFFFFFFFULL) | 0x8000000000000000ULL;
        return my;
    }
    inline uuid uuid1() {
        // Number of 100-ns intervals since 00:00:00.00 15 October 1582; [ref] uuid.py
        uint64_t ns100_intervals = get_time( 0x01b21dd213814000ULL );
        uint16_t clock_seq = (uint16_t)( ns100_intervals & 0x3fff );  // 14-bits max
        uint64_t mac = get_any_mac48();                               // 48-bits max
        uint32_t time_low = ns100_intervals & 0xffffffff;
        uint16_t time_mid = (ns100_intervals >> 32) & 0xffff;
        uint16_t time_hi_version = (ns100_intervals >> 48) & 0xfff;
        uint8_t clock_seq_low = clock_seq & 0xff;
        uint8_t clock_seq_hi_variant = (clock_seq >> 8) & 0x3f;
        uuid u;
        uint64_t &upper_ = u.ab;
        uint64_t &lower_ = u.cd;
        // Build the high 32 bytes
        upper_  = (uint64_t) time_low << 32;
        upper_ |= (uint64_t) time_mid << 16;
        upper_ |= (uint64_t) time_hi_version;
        // Build the low 32 bytes, using the clock sequence number
        lower_  = (uint64_t) ((clock_seq_hi_variant << 8) | clock_seq_low) << 48;
        lower_ |= mac;
        // Set the variant to RFC 4122.
        lower_ &= ~((uint64_t)0xc000 << 48);
        lower_ |=   (uint64_t)0x8000 << 48;
        // Set the version number.
        enum { version = 1 };
        upper_ &= ~0xf000;
        upper_ |= version << 12;
        return u;
    }
    inline uuid uuid0() {
        // Number of 100-ns intervals since Unix epoch time
        uint64_t ns100_intervals = get_time( 0 );
        uint64_t pid = $windows( _getpid() ) $welse( getpid() );
        uint16_t pid16 = (uint16_t)( pid & 0xffff ); // 16-bits max
        uint64_t mac = get_any_mac48();              // 48-bits max
        uint32_t time_low = ns100_intervals & 0xffffffff;
        uint16_t time_mid = (ns100_intervals >> 32) & 0xffff;
        uint16_t time_hi_version = (ns100_intervals >> 48) & 0xfff;
        uint8_t pid_low = pid16 & 0xff;
        uint8_t pid_hi = (pid16 >> 8) & 0xff;
        uuid u;
        uint64_t &upper_ = u.ab;
        uint64_t &lower_ = u.cd;
        // Build the high 32 bytes.
        upper_  = (uint64_t) time_low << 32;
        upper_ |= (uint64_t) time_mid << 16;
        upper_ |= (uint64_t) time_hi_version;
        // Build the low 32 bytes, using the mac and pid number.
        lower_  = (uint64_t) ((pid_hi << 8) | pid_low) << 48;
        lower_ |= mac;
        // Set the version number.
        enum { version = 0 };
        upper_ &= ~0xf000;
        upper_ |= version << 12;
        return u;
    }
    inline uuid rebuild( uint64_t ab, uint64_t cd ) {
        uuid u;
        u.ab = ab; u.cd = cd;
        return u;
    }
    inline uuid rebuild( const std::string &uustr ) {
        char sep;
        uint64_t a,b,c,d,e;
        uuid u = { 0, 0 };
        auto idx = uustr.find_first_of("-");
        if( idx != std::string::npos ) {
            // single separator, base62 notation
            if( uustr.find_first_of("-",idx+1) == std::string::npos ) {
                auto rebase62 = [&]( const char *input, size_t limit ) -> uint64_t {
                    int base62len = 10 + 26 + 26;
                    auto strpos = []( char ch ) -> size_t {
                        if( ch >= 'a' ) return ch - 'a' + 10 + 26;
                        if( ch >= 'A' ) return ch - 'A' + 10;
                        return ch - '0';
                    };
                    uint64_t res = strpos( input[0] );
                    for( size_t i = 1; i < limit; ++i )
                        res = base62len * res + strpos( input[i] );
                    return res;
                };
                u.ab = rebase62( &uustr[0], idx );
                u.cd = rebase62( &uustr[idx+1], uustr.size() - (idx+1) );
            }
            // else classic hex notation
            else {
                std::stringstream ss( uustr );
                if( ss >> std::hex >> a >> sep >> b >> sep >> c >> sep >> d >> sep >> e ) {
                    if( ss.eof() ) {
                        u.ab = (a << 32) | (b << 16) | c;
                        u.cd = (d << 48) | e;
                    }
                }
            }
        }
        return u;
    }
} // ::sole
#undef $bsd
#undef $belse
#undef $linux
#undef $lelse
#undef $osx
#undef $oelse
#undef $unix
#undef $uelse
#undef $windows
#undef $welse
#undef $yes
#undef $no
// Pop disabled warnings
#if (defined(__APPLE__) && defined(__MACH__))
#pragma clang diagnostic pop
#endif
#ifdef SOLE_BUILD_DEMO
// g++ users: `g++ demo.cc -std=c++11 -lrt -o sample`
// visual studio: `cl.exe demo.cc sole.cpp`
#include <iostream>
int main() {
    sole::uuid u0 = sole::uuid0(), u1 = sole::uuid1(), u4 = sole::uuid4();
    std::cout << "uuid v0 string : " << u0 << std::endl;
    std::cout << "uuid v0 base62 : " << u0.base62() << std::endl;
    std::cout << "uuid v0 pretty : " << u0.pretty() << std::endl << std::endl;
    std::cout << "uuid v1 string : " << u1 << std::endl;
    std::cout << "uuid v1 base62 : " << u1.base62() << std::endl;
    std::cout << "uuid v1 pretty : " << u1.pretty() << std::endl << std::endl;
    std::cout << "uuid v4 string : " << u4 << std::endl;
    std::cout << "uuid v4 base62 : " << u4.base62() << std::endl;
    std::cout << "uuid v4 pretty : " << u4.pretty() << std::endl << std::endl;
    u1 = sole::rebuild("F81D4FAE-7DEC-11D0-A765-00A0C91E6BF6");
    u4 = sole::rebuild("GITheR4tLlg-BagIW20DGja");
    std::cout << "uuid v1 rebuilt : " << u1 << " -> " << u1.pretty() << std::endl;
    std::cout << "uuid v4 rebuilt : " << u4 << " -> " << u4.pretty() << std::endl;
}
#endif
#ifdef SOLE_BUILD_TESTS
// g++ users: `g++ sole.cxx -std=c++11 -lrt -o tests`
// visual studio: `cl.exe sole.cxx`
#include <cassert>
#include <set>
#include <ratio>
#include <chrono>
#include <iostream>
using namespace sole;
namespace run
{
    auto epoch = [](){
        return std::chrono::system_clock::to_time_t( std::chrono::system_clock::now() );
    };
    template<typename FN>
    void benchmark( const FN &fn, const std::string &name ) {
        std::cout << "Benchmarking " << name << "... " << std::flush;
        auto then = epoch();
        while( epoch() == then );
        then = epoch();
        unsigned c = 0;
        while( epoch() == then ) c = ( fn(), ++c );
        std::cout << (c) << " uuids/sec" << std::endl;
    }
    template<typename FN>
    void tests( const FN &fn ) {
        unsigned numtests = ~0;
        std::cout << "Testing for " << numtests << " collisions... " << std::endl;
        auto then = epoch();
        std::set<uuid> all;
        for( unsigned i = 0; i < numtests; ++i ) {
            auto now = epoch();
            if( now != then ) {
                then = now;
                double pct6digs = ( int( ( double(i) / (unsigned)(~0) ) * 1e4 ) / double(1e4) );
                std::cout << '\r' << i << " uuids generated, no collision (" << pct6digs << "%)" << std::flush;
            }
            sole::uuid my_uuid = fn();
            assert( all.find(my_uuid) == all.end() && "error: UUIDs just collided! is std::random_device a real random generator?" );
            all.insert( my_uuid );
        }
    }
    template<typename FN>
    void verify( const FN &fn ) {
        std::cout << "Verifying serialization of 1 million UUIDs... " << std::flush;
        for( unsigned i = 0; i < 1000000; ++i ) {
            sole::uuid uuid = fn();
            sole::uuid rebuilt1 = sole::rebuild( uuid.str() );
            sole::uuid rebuilt2 = sole::rebuild( uuid.base62() );
            assert( rebuilt1 == uuid && "error: rebuild() or .str() failed" );
            assert( rebuilt2 == uuid && "error: rebuild() or .base62() failed" );
        }
        std::cout << "ok" << std::endl;
    }
}
int main() {
    assert( sizeof(sole::uuid      )  * 8 == 128 );
    assert( sizeof(sole::uuid0().ab)  * 8 ==  64 );
    assert( sizeof(sole::uuid0().cd)  * 8 ==  64 );
    run::benchmark(uuid0, "v0");
    run::benchmark(uuid1, "v1");
    run::benchmark(uuid4, "v4");
    run::verify(uuid4);             // use fastest implementation
//  run::tests(uuid0);              // not applicable
//  run::tests(uuid1);              // not applicable
    run::tests(uuid4);
}
#endif
csrc/cffmpeg.cpp
@@ -90,10 +90,10 @@
    s->BuildDecoder();
}
void* c_ffmpeg_get_pic_decoder(const cffmpeg h, int *wid, int *hei){
void* c_ffmpeg_get_pic_decoder(const cffmpeg h, int *wid, int *hei, int64_t *id){
    Wrapper *s = (Wrapper*)h;
    unsigned char *data = NULL;
    s->GetPicDecoder(&data, wid, hei);
    s->GetPicDecoder(&data, wid, hei, id);
    return data;
}
csrc/ffmpeg/format/FormatOut.cpp
@@ -339,7 +339,6 @@
            char option_key[]="movflags";
            char option_value[]="frag_keyframe+empty_moov";
            av_dict_set(&avdic,option_key,option_value,0);
            flag = writeHeader(&avdic);
            av_dict_free(&avdic);
@@ -365,6 +364,7 @@
    bool FormatOut::endWriter(){
        auto flag =  writeTrailer();
        closeResource();
        record_ = false;
        return flag;
@@ -397,12 +397,17 @@
        
        AVRational time_base_q = { 1, AV_TIME_BASE };
        //Duration between 2 frames (us)
        int64_t calc_duration = (double)(AV_TIME_BASE)*(1 / fps_);  //内部时间戳
        // int64_t calc_duration = (double)(AV_TIME_BASE)*(1 / fps_);  //内部时间戳
        int64_t calc_duration = (int64_t)(AV_TIME_BASE / fps_);  //内部时间戳
        //Parameters
        pkt.pts = av_rescale_q(time_stamp*calc_duration, time_base_q, time_base);
        pkt.dts = pkt.pts;
        pkt.duration = av_rescale_q(calc_duration, time_base_q, time_base); //(double)(calc_duration)*(double)(av_q2d(time_base_q)) / (double)(av_q2d(time_base));
        
        // if (pkt.duration < 0 || time_base.den != 90000){
            // logIt("CALCULATE DURATION : %lld, fame count : %lld, TIMEBASE: %d", calc_duration,time_stamp, time_base.den);
        // }
    }
    bool FormatOut::writeFrame(AVPacket &pkt, const int64_t &frame_cnt,
csrc/wrapper.cpp
@@ -39,6 +39,8 @@
    ,gb_(0)
    ,cpu_(0)
    ,use_decoder_(false)
    ,minduration(250)
    ,maxduration(750)
    {
        makeTheWorld();
    }
@@ -46,14 +48,29 @@
    Wrapper::~Wrapper()
    {
        if(thread_){
            stop_stream_.store(true);
            thread_->join();
        try
        {
            if(thread_){
                stop_stream_.store(true);
                thread_->join();
            }
            if(bridge_){
                delete bridge_; bridge_ = NULL;
            }
            map_rec_.clear();
            list_rec_pkt_.clear();
            for(auto &i : list_pic_){
                free(i.data);
            }
        }
        if(bridge_){
            delete bridge_; bridge_ = NULL;
        catch(const std::exception& e)
        {
            logIt("WRAPPER EXCEPTION: ", e.what());
        }
    }
    void Wrapper::ScalePicture(const int w, const int h, const int flags){
@@ -132,7 +149,10 @@
                auto data(std::make_shared<CodedData>());
                if(!in->readPacket(data)){
                    logIt("read packet error");
                    pkt.id = -1; data = nullptr; id = 0;
                    data.reset();
                    data = nullptr;
                    pkt.id = -1;
                    id = 0;
                }else{
                    pkt.id = id++;
                }
@@ -143,6 +163,13 @@
                run_worker(in.get(), pkt);
                if(!data){
                    {
                        std::lock_guard<std::mutex> l(mutex_rec_);
                        map_rec_.clear();
                    }
                    std::lock_guard<std::mutex> locker(mtx_rec_pkt_);
                    list_rec_pkt_.clear();
                    break;
                }
                //test
@@ -180,27 +207,72 @@
            auto ret = in->decode(frame, pkt.data);
            if(ret == 1){
                //吐出数据
                cache_pic(frame);
                cache_pic(frame, pkt.id);
            }
        }
        cache_rec_pkt(pkt);
        {
            std::lock_guard<std::mutex> l(mutex_rec_);
        for(auto &i : map_rec_){
            if (!i.second.rec){
                i.second.rec = i.second.fn_init(in);
            for(auto &i : map_rec_){
                if (!i.second.rec){
                    i.second.rec = std::move(init_recorder(in, i.second.rid, i.second.dir, i.second.min, i.second.max));
                    if (i.second.rec){
                        std::lock_guard<std::mutex> locker(mtx_rec_pkt_);
                        for(auto &k : list_rec_pkt_){
                            avpacket p = {k.data, k.id};
                            i.second.rec->CachePacket(p);
                        }
                        logIt("START REC %d FRAMES", list_rec_pkt_.size());
                    }
                }else if (i.second.rec){
                    i.second.rec->CachePacket(pkt);
                }
            }
            if (i.second.rec){
                i.second.rec->CachePacket(pkt);
        }
    }
    int Wrapper::cache_rec_pkt(const avpacket &pkt){
        std::lock_guard<std::mutex> locker(mtx_rec_pkt_);
        //wait I
        if (list_rec_pkt_.empty()) {
            AVPacket &avpkt = pkt.data->getAVPacket();
            if (!(avpkt.flags & AV_PKT_FLAG_KEY)){
                return -1;
            }
        }
        maybe_dump_rec_pkt();
        recpkt k = {pkt.data, pkt.id};
        list_rec_pkt_.push_back(k);
        return 0;
    }
    void Wrapper::maybe_dump_rec_pkt(){
        //超过min/2,丢弃gop
        while (list_rec_pkt_.size() > minduration) {
            list_rec_pkt_.pop_front();
            while(!list_rec_pkt_.empty()){
                auto &cache = list_rec_pkt_.front();
                AVPacket &avpkt = cache.data->getAVPacket();
                if (!(avpkt.flags & AV_PKT_FLAG_KEY)){
                    list_rec_pkt_.pop_front();
                }else{
                    break;
                }
            }
        }
    }
    //////////////recorder
    std::shared_ptr<Recorder> Wrapper::init_recorder(FormatIn *in, std::string id,std::string dir, const int mind, const int maxd){
    std::unique_ptr<Recorder> Wrapper::init_recorder(FormatIn *in, std::string id, std::string dir, const int mind, const int maxd){
        if(!in){
            logIt("Init wrapper first");
            return nullptr;
        }
        auto rec = std::make_shared<Recorder>(in, id);
        std::unique_ptr<Recorder> rec(new Recorder(in, id.c_str()));
        rec->SetCallback([&](std::string &id, int &index, std::string &path){
            cache_rec_info(id, index, path);
@@ -221,22 +293,51 @@
    void Wrapper::BuildRecorder(const char* id, const char *output, const int mindur, const int maxdur){
        std::string rid(id);
        std::string dir(output);
        auto fn = [=](FormatIn *in){
            return init_recorder(in, rid, dir, mindur, maxdur);
        };
        std::shared_ptr<Recorder> rec(nullptr);
        
        FnRec r = FnRec{fn, rec};
        map_rec_[rid] = r;
        std::lock_guard<std::mutex> l(mutex_rec_);
        // auto fn = [=](FormatIn *in){
        //     return init_recorder(in, rid, dir, mindur, maxdur);
        // };
        // FnRec r = FnRec{fn, nullptr};
        if (map_rec_.find(rid) != map_rec_.end()){
            map_rec_.erase(rid);
        }
        // for (auto iter = map_rec_.begin(); iter != map_rec_.end();){
        //     if (iter->second.rec && iter->second.rec->ErrorOcurred()){
        //         iter == map_rec_.erase(iter);
        //     }else{
        //         iter++;
        //     }
        // }
        FnRec fr;
        fr.rid = rid;
        fr.dir = dir;
        fr.min = mindur;
        fr.max = maxdur;
        map_rec_[rid] = std::move(fr);
        minduration = mindur * 25;
        maxduration = maxdur * 25;
    }
    int Wrapper::FireRecorder(const char* sid,const int64_t &id){
        std::lock_guard<std::mutex> l(mutex_rec_);
        auto iter = map_rec_.find(sid);
        if (iter != map_rec_.end()){
            if(iter->second.rec){
                iter->second.rec->FireRecorder(id);
            }
        }
        // for (auto iter = map_rec_.begin(); iter != map_rec_.end();){
        //     if (iter->second.rec && iter->second.rec->ErrorOcurred()){
        //         iter == map_rec_.erase(iter);
        //     }else{
        //         iter++;
        //     }
        // }
    }
    void Wrapper::cache_rec_info(std::string &id, int &index, std::string &path){
@@ -250,10 +351,11 @@
        struct record_file_info info;
        info.file_frame_index = index;
        info.file_path = path;
        info.rec_id = id;
        list_rec_.emplace_back(info);
        list_rec_map_[path] = id;
        logIt("list rec files count : %d", list_rec_.size());
        map_rec_.erase(id);
        logIt("LIST REC FILES COUNT : %d", list_rec_.size());
    }
    void Wrapper::GetInfoRecorder(int &index, std::string &path){
@@ -267,16 +369,32 @@
        index = info.file_frame_index;
        path = info.file_path;
        list_rec_.pop_front();
        if (map_rec_.find(info.rec_id) != map_rec_.end())
            map_rec_.erase(info.rec_id);
        for (auto iter = map_rec_.begin(); iter != map_rec_.end();){
            if (iter->second.rec && iter->second.rec->ErrorOcurred()){
                iter == map_rec_.erase(iter);
            }else{
                iter++;
            }
        }
        // logIt("go get info index: %d, file: %s\n", index, path.c_str());
    }
    std::string Wrapper::GetRecorderID(const std::string &path){
        std::lock_guard<std::mutex> l(mutex_rec_);
        std::string ret("");
        auto iter = list_rec_map_.find(path);
        if (iter != list_rec_map_.end()){
            ret = iter->second;
            list_rec_map_.erase(iter);
        }
        return ret;
    }
    ////////decoder
@@ -284,7 +402,7 @@
        use_decoder_ = true;
    }
    void Wrapper::cache_pic(std::shared_ptr<ffwrapper::FrameData> &frame){
    void Wrapper::cache_pic(std::shared_ptr<ffwrapper::FrameData> &frame, int64_t &id){
        pic_bgr24 pic;
        if(bridge_){
@@ -295,6 +413,7 @@
            unsigned char *data = (unsigned char*)malloc(pic.w * pic.h * 3);
            bridge_->copyPicture(data, frm);
            pic.data = data;
            pic.id = id;
        }
        
        {
@@ -311,7 +430,7 @@
    }
    void Wrapper::GetPicDecoder(unsigned char **data, int *w, int *h){
    void Wrapper::GetPicDecoder(unsigned char **data, int *w, int *h, int64_t *id){
        std::lock_guard<std::mutex> l(mutex_pic_);
        if(list_pic_.empty()){
            *data = NULL;
@@ -321,6 +440,7 @@
        }
        auto p = list_pic_.front();
        *data = p.data; *w = p.w; *h = p.h;
        *id = p.id;
        list_pic_.pop_front();
    }
csrc/wrapper.hpp
@@ -35,6 +35,8 @@
        unsigned char *data;
        int w;
        int h;
        int64_t id;
    }pic_bgr24;
    namespace buz{
@@ -42,11 +44,14 @@
        struct avpacket;
    }
    typedef std::function<std::shared_ptr<buz::Recorder>(ffwrapper::FormatIn*)> FN_REC;
    // typedef std::function<std::shared_ptr<buz::Recorder>(ffwrapper::FormatIn*)> FN_REC;
    typedef struct _fn_rec{
        FN_REC fn_init;
        std::shared_ptr<buz::Recorder> rec;
        std::string rid;
        std::string dir;
        int min;
        int max;
        std::unique_ptr<buz::Recorder> rec;
    }FnRec;
    class Wrapper{
@@ -58,10 +63,10 @@
            std::unique_ptr<ffwrapper::FormatIn> init_reader(const char* input);
            // ffwrapper::FormatIn* init_reader_gb28181(const char* input);
            void run_worker(ffwrapper::FormatIn *in, buz::avpacket &pkt);
            std::shared_ptr<buz::Recorder> init_recorder(ffwrapper::FormatIn *in, std::string id,std::string dir, const int mind, const int maxd);
            std::unique_ptr<buz::Recorder> init_recorder(ffwrapper::FormatIn *in, std::string id,std::string dir, const int mind, const int maxd);
            void cache_rec_info(std::string &id, int &index, std::string &path);
            void cache_pic(std::shared_ptr<ffwrapper::FrameData> &frame);
            void cache_pic(std::shared_ptr<ffwrapper::FrameData> &frame, int64_t &id);
            void cacheAVPacket(const AVPacket &pkt);
        public: 
@@ -80,7 +85,7 @@
            void UseCPU();
        public: //decoder
            void BuildDecoder();
            void GetPicDecoder(unsigned char **data, int *w, int *h);
            void GetPicDecoder(unsigned char **data, int *w, int *h, int64_t *id);
            void GetPacket(unsigned char **pktData, int *size, int *key);
            //active api
@@ -100,6 +105,7 @@
            struct record_file_info{
                int file_frame_index;
                std::string file_path;
                std::string rec_id;
            };
            std::list<struct record_file_info>  list_rec_;
            std::unordered_map<std::string, std::string> list_rec_map_;
@@ -114,6 +120,18 @@
            int scale_w_, scale_h_, scale_f_;
            int gb_, cpu_;
            typedef struct recpkt{
                std::shared_ptr<ffwrapper::CodedData> data;
                int64_t id;
            }recpkt;
            std::list<recpkt> list_rec_pkt_;
            std::mutex mtx_rec_pkt_;
            int cache_rec_pkt(const buz::avpacket &pkt);
            void maybe_dump_rec_pkt();
            int     maxduration;
            int     minduration;
        //////////////////test frame to bgr24
        public:
            uint8_t *decodeJPEG(const char *file, int *w, int *h);
goffmpeg.go
@@ -160,6 +160,7 @@
    return int(i), path
}
// GetRecID get rec id
func (h *GoFFMPEG) GetRecID(p string) string {
    pt := C.CString(p)
    defer C.free(unsafe.Pointer(pt))
@@ -179,20 +180,21 @@
}
// GetPicDecoder get pic from decoder
func (h *GoFFMPEG) GetPicDecoder() ([]byte, int, int) {
func (h *GoFFMPEG) GetPicDecoder() ([]byte, int, int, int64) {
    var width C.int
    var height C.int
    var fid C.long
    p := C.wrap_fn_decoder_pic(h.ffmpeg, &width, &height)
    p := C.wrap_fn_decoder_pic(h.ffmpeg, &width, &height, &fid)
    if width == 0 && height == 0 {
        return nil, 0, 0
        return nil, 0, 0, 0
    }
    defer C.free(unsafe.Pointer(p))
    d := C.GoBytes(p, width*height*3)
    wid := int(width)
    hei := int(height)
    return d, wid, hei
    gfid := int64(fid)
    return d, wid, hei, gfid
}
//GetAVPacket get AVPacket
libcffmpeg.c
@@ -109,8 +109,8 @@
    fn_decoder(h);
}
void* wrap_fn_decoder_pic(const cffmpeg h, int* wid, int* hei){
    return fn_decoder_pic(h, wid, hei);
void* wrap_fn_decoder_pic(const cffmpeg h, int* wid, int* hei, int64_t *id){
    return fn_decoder_pic(h, wid, hei, id);
}
void* wrap_fn_get_avpacket(const cffmpeg h, int* size, int* key){
libcffmpeg.h
@@ -21,7 +21,7 @@
typedef char*(*lib_cffmpeg_info_recorder)(const cffmpeg, int*, int*);
typedef char*(*lib_cffmpeg_rec_id)(const cffmpeg, const char*, int*);
typedef void (*lib_cffmpeg_decoder)(const cffmpeg);
typedef void*(*lib_cffmpeg_pic)(const cffmpeg, int*, int*);
typedef void*(*lib_cffmpeg_pic)(const cffmpeg, int*, int*, int64_t*);
typedef void*(*lib_cffmpeg_avpacket)(const cffmpeg, int*, int*);
typedef void*(*lib_cffmpeg_decode_jpeg)(const cffmpeg, const char*, int*, int*);
@@ -55,7 +55,7 @@
char* wrap_fn_info_recorder(const cffmpeg, int* index, int* length);
char* wrap_fn_rec_id(const cffmpeg h, const char* path, int*length);
void wrap_fn_decoder(const cffmpeg h);
void* wrap_fn_decoder_pic(const cffmpeg h, int* wid, int* hei);
void* wrap_fn_decoder_pic(const cffmpeg h, int* wid, int* hei, int64_t *id);
void* wrap_fn_get_avpacket(const cffmpeg h, int* size, int* key);
void* wrap_fn_decode_jpeg(const cffmpeg h, const char* file, int* wid, int* hei);