#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 #include "utf8codec.h" #include "../deprecated/StdCFile.h" #include "cpm/shared_ptr.h" #include #define CWD "." struct http_flv_live_t { char path[PATH_MAX]; std::shared_ptr buf; size_t cap; size_t len; http_session_t* http; std::shared_ptr writer; std::shared_ptr muxer; uint32_t pts, dts; std::shared_ptr content; std::vector > frames; // h264 frames std::vector >::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; }