stream-deploy/ZLM/3rdpart/media-server/libflv/test/http-flv-live.cpp

229 lines
5.4 KiB
C++

#include "mpeg4-aac.h"
#include "mpeg4-avc.h"
#include "mpeg4-hevc.h"
#include "flv-muxer.h"
#include "flv-writer.h"
#include "http-server.h"
#include "aio-worker.h"
#include "uri-parse.h"
#include "urlcodec.h"
#include "sys/thread.h"
#include "sys/system.h"
#include "sys/path.h"
#include "cstringext.h"
#include <stdio.h>
#include "utf8codec.h"
#include "../deprecated/StdCFile.h"
#include "cpm/shared_ptr.h"
#include <vector>
#define CWD "."
struct http_flv_live_t
{
char path[PATH_MAX];
std::shared_ptr<uint8_t> buf;
size_t cap;
size_t len;
http_session_t* http;
std::shared_ptr<void> writer;
std::shared_ptr<flv_muxer_t> muxer;
uint32_t pts, dts;
std::shared_ptr<void> content;
std::vector<std::pair<const uint8_t*, int> > frames; // h264 frames
std::vector<std::pair<const uint8_t*, int> >::const_iterator it;
const uint8_t* ptr;
int vcl;
};
static int http_flv_live_read(http_flv_live_t* live);
static int http_flv_live_send(http_flv_live_t* live);
static int http_flv_live_destroy(http_flv_live_t* live)
{
// TODO: close http sessoin
delete live;
return 0;
}
static int http_flv_live_onsend(void* param, int code, size_t bytes)
{
http_flv_live_t* live = (http_flv_live_t*)param;
if (0 != code)
{
printf("===============send error: %d===============\r\n", code);
http_flv_live_destroy(live);
return 0;
}
assert(live->len = bytes);
live->len = 0; // clear
system_sleep(40); // next frame
return http_flv_live_send(live);
}
static int http_flv_live_send(http_flv_live_t* live)
{
if (live->it == live->frames.end())
{
printf("===============done===============\r\n");
http_flv_live_destroy(live);
return 0; /*end*/
}
int r = flv_muxer_avc(live->muxer.get(), live->it->first, live->it->second, live->pts, live->dts);
live->pts += 40;
live->dts += 40;
live->it++;
if (0 != r)
{
printf("===============send error===============\r\n");
http_flv_live_destroy(live);
return r;
}
r = http_server_send(live->http, live->buf.get(), live->len, http_flv_live_onsend, live);
return 0 == r ? 1 /*more data*/ : r;
}
static int http_flv_write(void* param, const struct flv_vec_t* v, int n)
{
http_flv_live_t* live = (http_flv_live_t*)param;
assert(n <= 3);
for (int i = 0; i < n; i++)
{
if (live->len + v[i].len > live->cap)
{
// TODO: realloc
assert(0);
return -1;
}
// TODO: avoid memory copy
memcpy(live->buf.get() + live->len, v[i].ptr, v[i].len);
live->len += v[i].len;
}
return 0;
}
static int http_flv_muxer_handler(void* param, int type, const void* data, size_t bytes, uint32_t timestamp)
{
http_flv_live_t* live = (http_flv_live_t*)param;
return flv_writer_input(live->writer.get(), type, data, bytes, timestamp);
}
static int http_flv_live(void* http, http_session_t* session, const char* method, const char* path)
{
char buffer[1024];//[PATH_MAX];
http_flv_live_t* live = new http_flv_live_t();
struct uri_t* uri = uri_parse(path, (int)strlen(path));
url_decode(uri->path, -1, live->path, sizeof(live->path));
uri_free(uri);
int n = 0;
while (live->path[n] == '/') n++; // filter /
UTF8Decode utf8(live->path+n);
path_resolve(buffer, sizeof(buffer), utf8, CWD, strlen(CWD));
// TODO: path resolve to fix /rootpath/../pathtosystem -> /pathtosystem
path_realpath(buffer, live->path);
// read frames
int r = http_flv_live_read(live);
if (0 != r)
{
delete live;
http_server_set_status_code(session, r, NULL);
return http_server_send(session, "", 0, NULL, NULL);
}
live->http = session;
http_server_set_header(session, "Connection", "close"); // server close connection on finish
http_server_set_content_length(session, -1); // don't need content-length
live->pts = 0;
live->dts = 0;
live->it = live->frames.begin();
live->muxer.reset(flv_muxer_create(http_flv_muxer_handler, live), flv_muxer_destroy);
// write flv file header
live->writer.reset(flv_writer_create2(0, 1, http_flv_write, live), flv_writer_destroy);
return http_flv_live_send(live);
}
void http_flv_live_test(const char* ip, int port)
{
aio_worker_init(4);
http_server_t* http = http_server_create(ip, port);
http_server_set_handler(http, http_flv_live, http);
// http process
while ('q' != getchar())
{
}
http_server_destroy(http);
aio_worker_clean(4);
}
static void h264_handler(void* param, const uint8_t* nalu, size_t bytes)
{
http_flv_live_t* ctx = (http_flv_live_t*)param;
assert(ctx->ptr < nalu);
const uint8_t* ptr = nalu - 3;
//const uint8_t* end = (const uint8_t*)nalu + bytes;
uint8_t nalutype = nalu[0] & 0x1f;
if (ctx->vcl > 0 && h264_is_new_access_unit(nalu, bytes))
{
ctx->cap = ptr - ctx->ptr > ctx->cap ? ptr - ctx->ptr : ctx->cap; // max
ctx->frames.push_back(std::make_pair(ctx->ptr, ptr - ctx->ptr));
ctx->ptr = ptr;
ctx->vcl = 0;
}
if (1 <= nalutype && nalutype <= 5)
++ctx->vcl;
}
static int http_flv_live_read(http_flv_live_t* live)
{
StdCFile fp(live->path, "rb");
if (!fp.IsOpened())
return 404;
long n = fp.GetFileSize();
if (n >= 32 * 1024 * 1024)
return 401;
live->content.reset(fp.Read(), free);
if (!live->content.get())
return 500;
live->vcl = 0;
live->ptr = (const uint8_t*)live->content.get();
live->cap = 0;
live->len = 0;
if (strendswith(live->path, ".h264") || strendswith(live->path, ".264"))
{
mpeg4_h264_annexb_nalu(live->content.get(), n, h264_handler, live);
live->cap += 4 * 1024; // flv avc/aac sequence header
live->buf.reset((uint8_t*)malloc(live->cap), free);
}
else
{
return 500;
}
return 0;
}