From 1407b93895205c29fb5e823a02ae57198c40674f Mon Sep 17 00:00:00 2001 From: JDierkse Date: Sat, 18 Mar 2023 13:11:34 +0100 Subject: [PATCH] Initial Commit --- .gitignore | 4 + .gitmodules | 3 + API/WebAPI.cpp | 135 +++++++++++++++++++++++++++++++++ API/WebAPI.h | 26 +++++++ Application/StreamRecoder.cc | 141 +++++++++++++++++++++++++++++++++++ Application/Test.cc | 46 ++++++++++++ Libraries/CTPL | 1 + Libraries/FFmpeg | 1 + Libraries/Http | 1 + Libraries/Logging | 1 + Libraries/Utilities | 1 + Makefile | 1 + Makefile.conf | 23 ++++++ Makefiles | 1 + Recoder/Makefile | 1 + 15 files changed, 386 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 API/WebAPI.cpp create mode 100644 API/WebAPI.h create mode 100644 Application/StreamRecoder.cc create mode 100644 Application/Test.cc create mode 120000 Libraries/CTPL create mode 120000 Libraries/FFmpeg create mode 120000 Libraries/Http create mode 120000 Libraries/Logging create mode 120000 Libraries/Utilities create mode 120000 Makefile create mode 100644 Makefile.conf create mode 160000 Makefiles create mode 120000 Recoder/Makefile diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5969cd8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.o.* +*.d.* +*.a.* +.*.swp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..876e403 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Makefiles"] + path = Makefiles + url = https://gogs.dierkse.nl/JDierkse/Makefiles diff --git a/API/WebAPI.cpp b/API/WebAPI.cpp new file mode 100644 index 0000000..62c8ae8 --- /dev/null +++ b/API/WebAPI.cpp @@ -0,0 +1,135 @@ +#include "WebAPI.h" +#include +#include +#include + + +namespace StreamRecoder { +namespace API { + +// API Description + +// Status Query +// Goal: Provide status information based on POST data +// Use: DomoticaSite status update +// URL: http:///API/////// + +WebAPI::WebAPI() +{ +} + +WebAPI::~WebAPI() +{ +} + +Http::HttpServer::HttpReply WebAPI::ProcessQuery(ctpl::thread_pool* pThreadPool, const std::string& ffmpeg, const std::string& uri, const std::vector& postData) +{ + if (!StringAlgorithm::iequals(uri, "/api") && !StringAlgorithm::istarts_with(uri, "/api/")) + { + Http::HttpServer::HttpReply reply; + reply.status = Http::HttpServer::HttpReply::Status::Unauthorized; + return reply; + } + + Http::HttpServer::HttpReply reply; + reply.status = Http::HttpServer::HttpReply::Status::InternalServerError; + reply.content = "{\"result\": \"error\"}\r\n"; + +/* + try { + std::string apiURI = uri.substr(4); + + std::vector uriTokens = StringAlgorithm::split(apiURI, '/'); + + if (uriTokens.size() < 7) + return reply; + + std::string action = uriTokens[1]; + std::string brand = uriTokens[2]; + + Recorder::Settings settings; + settings.Path = path; + settings.IpAddress = uriTokens[3]; + settings.Port = uriTokens[4]; + settings.URL = cppcodec::base64_url::decode(uriTokens[5].c_str(), uriTokens[5].length()); + settings.Username = uriTokens[6]; + settings.Password = uriTokens[7]; + + int numberOfImages = 5; + if (uriTokens.size() > 8) + numberOfImages = stoi(uriTokens[8]); + + reply.status = Http::HttpServer::HttpReply::Status::Ok; + if (StringAlgorithm::iequals("Digoo", brand)) + { + auto recorder = Recorder::DigooRecorder(pHttpClient, settings); + if (StringAlgorithm::iequals("Snapshot", action)) + reply.content = recorder.Snapshot(pThreadPool, ffmpeg); + else if (StringAlgorithm::iequals("MultiSnapshot", action)) + reply.content = recorder.MultiSnapshot(pThreadPool, ffmpeg, numberOfImages); + else if (StringAlgorithm::iequals("Video", action)) + reply.content = recorder.Video(pThreadPool, ffmpeg); + } + else if (StringAlgorithm::iequals("Foscam", brand)) + { + auto recorder = Recorder::FoscamRecorder(pHttpClient, settings); + if (StringAlgorithm::iequals("Snapshot", action)) + reply.content = recorder.Snapshot(pThreadPool, ffmpeg); + else if (StringAlgorithm::iequals("MultiSnapshot", action)) + reply.content = recorder.MultiSnapshot(pThreadPool, ffmpeg, numberOfImages); + else if (StringAlgorithm::iequals("Video", action)) + reply.content = recorder.Video(pThreadPool, ffmpeg); + } + else if (StringAlgorithm::iequals("RTSP", brand)) + { + auto recorder = Recorder::RTSPRecorder(pHttpClient, settings); + if (StringAlgorithm::iequals("Snapshot", action)) + reply.content = recorder.Snapshot(pThreadPool, ffmpeg); + else if (StringAlgorithm::iequals("MultiSnapshot", action)) + reply.content = recorder.MultiSnapshot(pThreadPool, ffmpeg, numberOfImages); + else if (StringAlgorithm::iequals("Video", action)) + reply.content = recorder.Video(pThreadPool, ffmpeg); + } + else if (StringAlgorithm::iequals("VStarCam", brand)) + { + auto recorder = Recorder::VStarCamRecorder(pHttpClient, settings); + if (StringAlgorithm::iequals("Snapshot", action)) + reply.content = recorder.Snapshot(pThreadPool, ffmpeg); + else if (StringAlgorithm::iequals("MultiSnapshot", action)) + reply.content = recorder.MultiSnapshot(pThreadPool, ffmpeg, numberOfImages); + else if (StringAlgorithm::iequals("Video", action)) + reply.content = recorder.Video(pThreadPool, ffmpeg); + } + else if (StringAlgorithm::iequals("WatchBot", brand)) + { + auto recorder = Recorder::WatchBotRecorder(pHttpClient, settings); + if (StringAlgorithm::iequals("Snapshot", action)) + reply.content = recorder.Snapshot(pThreadPool, ffmpeg); + else if (StringAlgorithm::iequals("MultiSnapshot", action)) + reply.content = recorder.MultiSnapshot(pThreadPool, ffmpeg, numberOfImages); + else if (StringAlgorithm::iequals("Video", action)) + reply.content = recorder.Video(pThreadPool, ffmpeg); + } + else if (StringAlgorithm::iequals("ZModo", brand)) + { + auto recorder = Recorder::ZModoRecorder(pHttpClient, settings); + if (StringAlgorithm::iequals("Snapshot", action)) + reply.content = recorder.Snapshot(pThreadPool, ffmpeg); + else if (StringAlgorithm::iequals("MultiSnapshot", action)) + reply.content = recorder.MultiSnapshot(pThreadPool, ffmpeg, numberOfImages); + else if (StringAlgorithm::iequals("Video", action)) + reply.content = recorder.Video(pThreadPool, ffmpeg); + } + } + catch (const std::exception& e) + { + std::cout << "WebAPI::ProcessQuery() - Error: " << e.what() << std::endl; + return reply; + } +*/ + + return reply; +} + +} // namespace API +} // namespace StreamRecoder diff --git a/API/WebAPI.h b/API/WebAPI.h new file mode 100644 index 0000000..47c9248 --- /dev/null +++ b/API/WebAPI.h @@ -0,0 +1,26 @@ +#ifndef API_WEBAPI_H +#define API_WEBAPI_H + +#include +#include +#include +#include +#include + + +namespace StreamRecoder { +namespace API { + +class WebAPI +{ +public: + WebAPI(); + ~WebAPI(); + + static Http::HttpServer::HttpReply ProcessQuery(ctpl::thread_pool* pThreadPool, const std::string& ffmpeg, const std::string& uri, const std::vector& postData); +}; + +} // namespace API +} // namespace StreamRecoder + +#endif // API_WEBAPI_H diff --git a/Application/StreamRecoder.cc b/Application/StreamRecoder.cc new file mode 100644 index 0000000..783a95b --- /dev/null +++ b/Application/StreamRecoder.cc @@ -0,0 +1,141 @@ +#include +#include +#include +#include +#include + +extern "C" { +#include +#include +#include +#include +} + +int main(int argc, char** argv) { + + // Open the initial context variables that are needed + SwsContext *img_convert_ctx; + AVFormatContext* format_ctx = avformat_alloc_context(); + AVCodecContext* codec_ctx = NULL; + int video_stream_index; + + // Register everything + av_register_all(); + avformat_network_init(); + + //open RTSP + if (avformat_open_input(&format_ctx, "rtsp://134.169.178.187:8554/h264.3gp", + NULL, NULL) != 0) { + return EXIT_FAILURE; + } + + if (avformat_find_stream_info(format_ctx, NULL) < 0) { + return EXIT_FAILURE; + } + + //search video stream + for (int i = 0; i < format_ctx->nb_streams; i++) { + if (format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) + video_stream_index = i; + } + + AVPacket packet; + av_init_packet(&packet); + + //open output file + AVFormatContext* output_ctx = avformat_alloc_context(); + + AVStream* stream = NULL; + int cnt = 0; + + //start reading packets from stream and write them to file + av_read_play(format_ctx); //play RTSP + + // Get the codec + AVCodec *codec = NULL; + codec = avcodec_find_decoder(AV_CODEC_ID_H264); + if (!codec) { + exit(1); + } + + // Add this to allocate the context by codec + codec_ctx = avcodec_alloc_context3(codec); + + avcodec_get_context_defaults3(codec_ctx, codec); + avcodec_copy_context(codec_ctx, format_ctx->streams[video_stream_index]->codec); + std::ofstream output_file; + + if (avcodec_open2(codec_ctx, codec, NULL) < 0) + exit(1); + + img_convert_ctx = sws_getContext(codec_ctx->width, codec_ctx->height, + codec_ctx->pix_fmt, codec_ctx->width, codec_ctx->height, AV_PIX_FMT_RGB24, + SWS_BICUBIC, NULL, NULL, NULL); + + int size = avpicture_get_size(AV_PIX_FMT_YUV420P, codec_ctx->width, + codec_ctx->height); + uint8_t* picture_buffer = (uint8_t*) (av_malloc(size)); + AVFrame* picture = av_frame_alloc(); + AVFrame* picture_rgb = av_frame_alloc(); + int size2 = avpicture_get_size(AV_PIX_FMT_RGB24, codec_ctx->width, + codec_ctx->height); + uint8_t* picture_buffer_2 = (uint8_t*) (av_malloc(size2)); + avpicture_fill((AVPicture *) picture, picture_buffer, AV_PIX_FMT_YUV420P, + codec_ctx->width, codec_ctx->height); + avpicture_fill((AVPicture *) picture_rgb, picture_buffer_2, AV_PIX_FMT_RGB24, + codec_ctx->width, codec_ctx->height); + + while (av_read_frame(format_ctx, &packet) >= 0 && cnt < 1000) { //read ~ 1000 frames + + std::cout << "1 Frame: " << cnt << std::endl; + if (packet.stream_index == video_stream_index) { //packet is video + std::cout << "2 Is Video" << std::endl; + if (stream == NULL) { //create stream in file + std::cout << "3 create stream" << std::endl; + stream = avformat_new_stream(output_ctx, + format_ctx->streams[video_stream_index]->codec->codec); + avcodec_copy_context(stream->codec, + format_ctx->streams[video_stream_index]->codec); + stream->sample_aspect_ratio = + format_ctx->streams[video_stream_index]->codec->sample_aspect_ratio; + } + int check = 0; + packet.stream_index = stream->id; + std::cout << "4 decoding" << std::endl; + int result = avcodec_decode_video2(codec_ctx, picture, &check, &packet); + std::cout << "Bytes decoded " << result << " check " << check + << std::endl; + if (cnt > 100) //cnt < 0) + { + sws_scale(img_convert_ctx, picture->data, picture->linesize, 0, + codec_ctx->height, picture_rgb->data, picture_rgb->linesize); + std::stringstream file_name; + file_name << "test" << cnt << ".ppm"; + output_file.open(file_name.str().c_str()); + output_file << "P3 " << codec_ctx->width << " " << codec_ctx->height + << " 255\n"; + for (int y = 0; y < codec_ctx->height; y++) { + for (int x = 0; x < codec_ctx->width * 3; x++) + output_file + << (int) (picture_rgb->data[0] + + y * picture_rgb->linesize[0])[x] << " "; + } + output_file.close(); + } + cnt++; + } + av_free_packet(&packet); + av_init_packet(&packet); + } + av_free(picture); + av_free(picture_rgb); + av_free(picture_buffer); + av_free(picture_buffer_2); + + av_read_pause(format_ctx); + avio_close(output_ctx->pb); + avformat_free_context(output_ctx); + + return (EXIT_SUCCESS); +} + diff --git a/Application/Test.cc b/Application/Test.cc new file mode 100644 index 0000000..9109761 --- /dev/null +++ b/Application/Test.cc @@ -0,0 +1,46 @@ +#include "API/WebAPI.h" +#include +#include +#include +#include +#include + + +int main(int argc, char** argv) +{ + try + { + Logging::OpenLog(); + Logging::SetLogMask(Logging::Severity::Debug); + + int port(8000); + ctpl::thread_pool threadPool(6); + std::string ffmpeg = "ffmpeg"; + + Http::HttpServer::CallbackMethod callback = std::bind(&StreamRecoder::API::WebAPI::ProcessQuery, &threadPool, ffmpeg, std::placeholders::_1, std::placeholders::_2); + + Http::HttpServer server(port, callback); + Logging::Log(Logging::Severity::Info, "Startup Complete"); + server.Wait(); + + Logging::Log(Logging::Severity::Info, "Stopping StreamRecoder..."); + + Logging::CloseLog(); + } + catch (const std::exception& e) + { + std::cerr << "Exception caught" << std::endl; + + std::stringstream ss; + ss << "Type : " << typeid(e).name() << std::endl; + ss << "ERROR: " << e.what() << std::endl; + + Logging::Log(Logging::Severity::Error, ss.str()); + + Logging::CloseLog(); + + return -1; + } + + return 0; +} diff --git a/Libraries/CTPL b/Libraries/CTPL new file mode 120000 index 0000000..61ac13c --- /dev/null +++ b/Libraries/CTPL @@ -0,0 +1 @@ +../../Libraries/CTPL \ No newline at end of file diff --git a/Libraries/FFmpeg b/Libraries/FFmpeg new file mode 120000 index 0000000..6c6c0b4 --- /dev/null +++ b/Libraries/FFmpeg @@ -0,0 +1 @@ +../../Libraries/FFmpeg \ No newline at end of file diff --git a/Libraries/Http b/Libraries/Http new file mode 120000 index 0000000..5fe3319 --- /dev/null +++ b/Libraries/Http @@ -0,0 +1 @@ +../../Libraries/Http \ No newline at end of file diff --git a/Libraries/Logging b/Libraries/Logging new file mode 120000 index 0000000..2b4f659 --- /dev/null +++ b/Libraries/Logging @@ -0,0 +1 @@ +../../Libraries/Logging \ No newline at end of file diff --git a/Libraries/Utilities b/Libraries/Utilities new file mode 120000 index 0000000..88f16bc --- /dev/null +++ b/Libraries/Utilities @@ -0,0 +1 @@ +../../Libraries/Utilities \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 120000 index 0000000..6aa5187 --- /dev/null +++ b/Makefile @@ -0,0 +1 @@ +Makefiles/Makefile \ No newline at end of file diff --git a/Makefile.conf b/Makefile.conf new file mode 100644 index 0000000..1a004b3 --- /dev/null +++ b/Makefile.conf @@ -0,0 +1,23 @@ +# +# Makefile.conf +# + +LIBRARIES += Http +LIBRARIES += Logging +LIBRARIES += Utilities + +CFLAGS += -I$(ROOTPATH)/Libraries/CTPL +CFLAGS += -I$(ROOTPATH)/Libraries/FFmpeg + +LFLAGS += -pthread + +CFLAGS += -I$(ROOTPATH) -I$(ROOTPATH)/Libraries + +CFLAGS += -Wno-sign-compare -Wno-deprecated-declarations + +DEBUGDIR := .debug + +#DEFAULTARCH := x86_64-alpine +DEFAULTARCH := x86_64 +TARGETS += StreamRecoder + diff --git a/Makefiles b/Makefiles new file mode 160000 index 0000000..df68de6 --- /dev/null +++ b/Makefiles @@ -0,0 +1 @@ +Subproject commit df68de61fdca1ad55a2dd9b8c7e4eb88f60c45f0 diff --git a/Recoder/Makefile b/Recoder/Makefile new file mode 120000 index 0000000..d0b0e8e --- /dev/null +++ b/Recoder/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file