{"id":20172613,"url":"https://github.com/igorrendulic/video-encoder-decoder","last_synced_at":"2026-05-05T16:36:33.277Z","repository":{"id":41483266,"uuid":"509837447","full_name":"igorrendulic/video-encoder-decoder","owner":"igorrendulic","description":"FFmpeg, Libav library for video decoding, encoding and remux-ing from multiple cameras","archived":false,"fork":false,"pushed_at":"2022-07-08T22:37:23.000Z","size":147,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-13T15:51:29.300Z","etag":null,"topics":["ffmpeg","libav","muxing","video-processing-service","video-streaming"],"latest_commit_sha":null,"homepage":"https://igor.technology","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/igorrendulic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-07-02T18:52:03.000Z","updated_at":"2023-04-10T11:52:05.000Z","dependencies_parsed_at":"2022-08-25T10:40:49.602Z","dependency_job_id":null,"html_url":"https://github.com/igorrendulic/video-encoder-decoder","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorrendulic%2Fvideo-encoder-decoder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorrendulic%2Fvideo-encoder-decoder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorrendulic%2Fvideo-encoder-decoder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorrendulic%2Fvideo-encoder-decoder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/igorrendulic","download_url":"https://codeload.github.com/igorrendulic/video-encoder-decoder/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241610977,"owners_count":19990505,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ffmpeg","libav","muxing","video-processing-service","video-streaming"],"created_at":"2024-11-14T01:31:34.502Z","updated_at":"2025-11-29T16:03:41.726Z","avatar_url":"https://github.com/igorrendulic.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# igor.technology Video Service and Library (ITVSL)\n\n[![stability-experimental](https://img.shields.io/badge/stability-experimental-orange.svg)](https://github.com/mkenney/software-guides/blob/master/STABILITY-BADGES.md#experimental)\n[![stability-wip](https://img.shields.io/badge/stability-wip-lightgrey.svg)](https://github.com/mkenney/software-guides/blob/master/STABILITY-BADGES.md#work-in-progress)\n[![c++](https://img.shields.io/badge/C++-Solutions-blue.svg?style=flat\u0026logo=c%2B%2B)](https://img.shields.io/badge/C++-Solutions-blue.svg?style=flat\u0026logo=c%2B%2B)\n\n_This is works in progress._\n\nThe idea behind the `ITVSL` (Igor Technology Video Service/Library) is to have a data format wrapped around `proto` where machine learning inference on a video is made once. The video may then be streamed live or on demand with inference information wrapped within it.\n\n## Install Protobuf\n\nsudo apt install protobuf-compiler\n\nCurrent format:\n\n```proto\nsyntax = \"proto3\";\n\npackage itvsl.protocol.v1beta1;\n\n\n// This structure stores compressed data.\n// For video, it should typically contain one compressed frame.\n// For audio it may contain several compressed frames.\n// Encoders are allowed to output empty packets, with no compressed data, containing only side data (e.g. to update some stream parameters at the end of encoding).\nmessage CPacket {\n    int64 pts  = 1; // Presentation timestamp in AVStream-\u003etime_base units; the time at which the decompressed packet will be presented to the user.\n    int64 dts = 2; // Decompression timestamp in AVStream-\u003etime_base units; the time at which the packet is decompressed.\n    bytes data = 3;\n    int32 size = 4;\n    int32 stream_id = 5;\n    int32 flags = 6; // A combination of AV_PKT_FLAG values.\n    bytes side_data = 7;\n    int32 side_data_elems = 8;\n    int64 duration = 9; // Duration of this packet in AVStream-\u003etime_base units, 0 if unknown.\n    int64 pos = 10; // byte position of stream, -1 if unknown\n    string meta = 11; // custom metadata if needed\n    bytes raw_bgr24 = 12; // raw bgr image\n}\n\n// Video Streaming messages\nmessage ShapeProto {\n    message Dim {\n        // Size of image in that dimension (-1 means unknown dimension)\n        int64 size = 1;\n        // optional name of image dimension\n        string name = 2;\n    }\n\n    repeated Dim dim = 2;\n}\n\nmessage VideoFrame {\n    int64 width = 1;\n    int64 height = 2;\n    bytes data = 3;\n    int64 timestamp = 4;\n    bool is_keyframe = 5;\n    int64 pts = 6;\n    int64 dts = 7;\n    string frame_type = 8;\n    bool is_corrupt = 9;\n    double time_base = 10;\n    ShapeProto shape = 11;\n    string device_id = 12;\n    int64 packet = 13;\n    int64 keyframe = 14;\n    bytes extradata = 15;\n    string codec_name = 16;\n    string pix_fmt = 17;\n}\n\n// VideoCodec information about the stream\nmessage VideoCodec {\n    string name = 1;\n    int32 width = 2;\n    int32 height = 3;\n    string pix_fmt = 4;\n    bytes extradata = 5;\n    int32 extradata_size = 6;\n    string long_name = 7;\n}\n```\n\n## Compile Proto\n\nFirst proto files must be compile and moved to `itvsl` root project. (/itvsl folder)\n\n```\ncd proto\ncmake .\nmake\ncp itvslprotocol.pb.* ../\n```\n\n## Debug with Valgrind\n\n```\nrm valgrind-out.txt\nvalgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=valgrind-out.txt ./myapp\n```\n\n## Examples\n\n### RTSP Camera with MP4 segments\n\n```c\n#include \u003ciostream\u003e\n#include \u003cchrono\u003e\n#include \u003cmemory\u003e\n#include \u003ccsignal\u003e\n#include \u003cfuture\u003e\n\n#include \u003citvsl.h\u003e\n#include \"itvsl/itvsl_archive.h\"\n#include \"itvsl/itvsl_queue.h\"\n#include \"itvsl/mp4_file_cleanup.h\"\n\n// using namespace std::placeholders;\n// using namespace std::this_thread;     // sleep_for, sleep_until\nusing namespace std::chrono_literals; // ns, us, ms, s, h, etc.\nusing namespace std::chrono;\n\nusing namespace itvsl::queue;\nusing namespace itvsl::archive;\nusing namespace itvsl::media;\nusing namespace itvsl::log;\n\n// struct SwsContext *img_convert_ctx;\n    // img_convert_ctx = sws_getCachedContext(NULL,\n        // pFrame-\u003ewidth, pFrame-\u003eheight, AV_PIX_FMT_YUV420P,\n        // pFrame-\u003ewidth, pFrame-\u003eheight, AV_PIX_FMT_BGR24,\n        // SWS_BICUBIC, NULL, NULL, NULL);\n\n// sws_scale(img_convert_ctx,\n//         pFrame-\u003edata,\n//         pFrame-\u003elinesize, 0, pFrame-\u003eheight,\n//         pFrameBGR-\u003edata,\n//         pFrameBGR-\u003elinesize);\n//     sws_freeContext(img_convert_ctx);\n\nnamespace {\n\n    // std::future\u003cvoid\u003e can be used to the thread, and it should exit when value in future is available.\n    std::promise\u003cvoid\u003e exitSignal;\n\n    // global definitions for easier cleanup after termination signal\n    itvsl::media::LibItvsl *cms = new LibItvsl();\n    StreamingContext *decoder = cms-\u003egetDecodingContext();\n\n    // archive inits\n    ItvslArchive ca;\n    Queue\u003cAVPacket\u003e archiveQueue;\n\n    // mp4 cleanup inits\n    Mp4FileCleanup mp4Cleanup;\n\n    AVPacket *input_packet = av_packet_alloc();\n}\n\nvoid signalHandler( int signum ) {\n\n    logging(\"exiting VSL with signum %d\", signum);\n\n\n    exitSignal.set_value();\n\n    AVPacket *pkt = av_packet_alloc();\n    pkt-\u003estream_index = 123456789;\n    archiveQueue.push(*pkt);\n\n    if (input_packet != NULL) {\n        av_packet_free(\u0026input_packet);\n        input_packet = NULL;\n    }\n\n    delete cms;\n    google::protobuf::ShutdownProtobufLibrary();\n\n   exit(signum);\n}\n\nint main() {\n\n    logging(\"**************************************************************************************\");\n    logging(\"*       Tool : ITVSL Media Streams                                                    *\");\n    logging(\"*     Author : Igor Rendulic (https://igor.technology)                                *\");\n    logging(\"*  Used Libs : FFmpeg/libav                                                           *\");\n    logging(\"**************************************************************************************\");\n    logging(\"--------------------------------------------------------------------------------------\");\n    logging(\"\");\n    logging(\"--------------------------------------------------------------------------------------\");\n\n\n    signal(SIGINT, signalHandler);\n\n    av_log_set_level(AV_LOG_DEBUG);\n    // av_log_set_level(AV_LOG_TRACE);\n\n    StreamingParams params = {0};\n    params.copy_audio = 0;\n    params.copy_video = 1;\n\n    char* video_codec = (char*)\"h264\";\n    char* codec_priv_key = (char*)\"h264-params\";\n\n    if (cms-\u003eopenInput(RTSP, \"rtsp://root:pass@10.0.1.26/axis-media/media.amp\", \u0026decoder-\u003epFormatContext) \u003c 0) {\n        logging(\"failed to open input stream\");\n        return -1;\n    };\n\n    if (cms-\u003eprepareDecoder(decoder)) {\n        logging(\"failed to prepare decoder\");\n        return -1;\n    }\n\n    // // optinally start archiving threads (storing mp4s and cleaning them up)\n    // std::future\u003cvoid\u003e termFuture = exitSignal.get_future();\n\n    // std::thread archiveThread(\u0026ItvslArchive::processMedia, \u0026ca, std::ref(archiveQueue), std::ref(*itvsl), std::move(termFuture));\n\n    // std::thread mp4cleanupThread(\u0026Mp4FileCleanup::processCleanup, \u0026mp4Cleanup, \".\", \"10s\");\n    // mp4cleanupThread.detach();\n\n    if (!input_packet) {logging(\"failed to allocated memory for AVPacket\"); return -1;}\n\n    print_timing(\"decoder\", decoder-\u003epFormatContext, decoder-\u003evideo_codec_context, decoder-\u003evideo_stream);\n\n    bool first_keyframe = true;\n\n    AVFrame *pFrame = av_frame_alloc();\n    if (!pFrame) {\n        logging(\"failed to allocated memory for AVFrame\");\n        return -1;\n    }\n\n    while (av_read_frame(decoder-\u003epFormatContext, input_packet) \u003e= 0)\n    {\n\n        if (input_packet-\u003estream_index == decoder-\u003evideo_index) {\n            // logging(\"AVPacket-\u003epts %\" PRId64, input_-\u003epts);\n\n            // if (input_packet-\u003eflags \u0026 AV_PKT_FLAG_KEY) {\n            //     // AV_PKT_FLAG_CORRUPT\n            // }\n\n            int response = cms-\u003edecode(decoder-\u003evideo_codec_context, pFrame, input_packet);\n            if (response \u003c 0) {\n                break;\n            }\n        }\n\n        // CPacket *cPacket = itvsl-\u003eitvslPacketize(input_packet);\n        // std::string cPacketBinary;\n        // cPacket-\u003eSerializeToString(\u0026cPacketBinary);\n\n        // CPacket testPacket;\n        // const std::string test = cPacketBinary;\n        // testPacket.ParseFromString(test);\n\n        // AVPacket *archive_packet = av_packet_clone(input_packet);\n\n        // archiveQueue.push(*archive_packet);\n\n        av_packet_unref(input_packet);\n    }\n\n    // archiveThread.join();\n\n    if (input_packet != NULL) {\n        av_packet_free(\u0026input_packet);\n        input_packet = NULL;\n    }\n    if (pFrame != NULL) {\n        av_frame_free(\u0026pFrame);\n    }\n\n    delete vsl;\n\n    google::protobuf::ShutdownProtobufLibrary();\n\n    return 0;\n}\n```\n\n## Resources\n\nnice decoder here: [https://github.com/DaWelter/h264decoder/blob/master/src/h264decoder.cpp](https://github.com/DaWelter/h264decoder/blob/master/src/h264decoder.cpp)\n\nffmpeg configure: [https://github.com/FFmpeg/FFmpeg/blob/master/configure](https://github.com/FFmpeg/FFmpeg/blob/master/configure)\n\nLivestreaming with libav: [https://blog.mi.hdm-stuttgart.de/index.php/2018/03/21/livestreaming-with-libav-tutorial-part-2/](https://blog.mi.hdm-stuttgart.de/index.php/2018/03/21/livestreaming-with-libav-tutorial-part-2/)\n\nThis project is based on: [https://github.com/leandromoreira/ffmpeg-libav-tutorial/blob/master/3_transcoding.c](https://github.com/leandromoreira/ffmpeg-libav-tutorial/blob/master/3_transcoding.c)\n\ncheck this transcoding example for audio maybe: [https://ffmpeg.org/doxygen/2.4/transcoding_8c_source.html](https://ffmpeg.org/doxygen/2.4/transcoding_8c_source.html)\n\nlist of examples: [https://github.com/FFmpeg/FFmpeg/tree/master/doc/examples](https://github.com/FFmpeg/FFmpeg/tree/master/doc/examples)\n\nextracting motion vectors: https://github.com/jishnujayakumar/MV-Tractus/blob/master/extract_mvs.c\n\ncompressed video action recognition: https://github.com/chaoyuaw/pytorch-coviar\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figorrendulic%2Fvideo-encoder-decoder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Figorrendulic%2Fvideo-encoder-decoder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figorrendulic%2Fvideo-encoder-decoder/lists"}