Initial Commit
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
*.o.*
|
||||||
|
*.d.*
|
||||||
|
*.a.*
|
||||||
|
.*.swp
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "Makefiles"]
|
||||||
|
path = Makefiles
|
||||||
|
url = https://gogs.dierkse.nl/JDierkse/Makefiles
|
||||||
+135
@@ -0,0 +1,135 @@
|
|||||||
|
#include "WebAPI.h"
|
||||||
|
#include <StringAlgorithm.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
|
||||||
|
namespace StreamRecoder {
|
||||||
|
namespace API {
|
||||||
|
|
||||||
|
// API Description
|
||||||
|
|
||||||
|
// Status Query
|
||||||
|
// Goal: Provide status information based on POST data
|
||||||
|
// Use: DomoticaSite status update
|
||||||
|
// URL: http://<ip>/API/<action>/<brand>/<ipaddress>/<port>/<url(base64)>/<username>/<password>
|
||||||
|
|
||||||
|
WebAPI::WebAPI()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
WebAPI::~WebAPI()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Http::HttpServer::HttpReply WebAPI::ProcessQuery(ctpl::thread_pool* pThreadPool, const std::string& ffmpeg, const std::string& uri, const std::vector<Http::HttpPostData>& 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<std::string> 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<std::string>(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
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#ifndef API_WEBAPI_H
|
||||||
|
#define API_WEBAPI_H
|
||||||
|
|
||||||
|
#include <HttpPostData.h>
|
||||||
|
#include <HttpServer.h>
|
||||||
|
#include <ctpl_stl.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
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<Http::HttpPostData>& postData);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace API
|
||||||
|
} // namespace StreamRecoder
|
||||||
|
|
||||||
|
#endif // API_WEBAPI_H
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
#include <libavformat/avio.h>
|
||||||
|
#include <libswscale/swscale.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
#include "API/WebAPI.h"
|
||||||
|
#include <ctpl_stl.h>
|
||||||
|
#include <Logging.h>
|
||||||
|
#include <HttpServer.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../../Libraries/CTPL
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../../Libraries/FFmpeg
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../../Libraries/Http
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../../Libraries/Logging
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../../Libraries/Utilities
|
||||||
@@ -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
|
||||||
|
|
||||||
Submodule
+1
Submodule Makefiles added at df68de61fd
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../Makefile
|
||||||
Reference in New Issue
Block a user