diff --git a/api/include/mk_h264_splitter.h b/api/include/mk_h264_splitter.h index 514992c9..0692e321 100644 --- a/api/include/mk_h264_splitter.h +++ b/api/include/mk_h264_splitter.h @@ -32,9 +32,10 @@ typedef void(API_CALL *on_mk_h264_splitter_frame)(void *user_data, mk_h264_split * 创建h264分帧器 * @param cb 分帧回调函数 * @param user_data 回调用户数据指针 + * @param is_h265 是否是265 * @return 分帧器对象 */ -API_EXPORT mk_h264_splitter API_CALL mk_h264_splitter_create(on_mk_h264_splitter_frame cb, void *user_data); +API_EXPORT mk_h264_splitter API_CALL mk_h264_splitter_create(on_mk_h264_splitter_frame cb, void *user_data, int is_h265); /** * 删除h264分帧器 diff --git a/api/source/mk_h264_splitter.cpp b/api/source/mk_h264_splitter.cpp index bf548ad1..c6229fdc 100644 --- a/api/source/mk_h264_splitter.cpp +++ b/api/source/mk_h264_splitter.cpp @@ -10,13 +10,15 @@ #include "mk_h264_splitter.h" #include "Http/HttpRequestSplitter.h" +#include "Extension/H264.h" +#include "Extension/H265.h" using namespace mediakit; class H264Splitter : public HttpRequestSplitter { public: using onH264 = std::function; - H264Splitter() = default; + H264Splitter(bool h265 = false) { _h265 = h265; } ~H264Splitter() override; void setOnSplitted(onH264 cb); @@ -25,7 +27,9 @@ protected: const char *onSearchPacketTail(const char *data, size_t len) override; private: + bool _h265 = false; onH264 _cb; + size_t _search_pos = 0; }; void H264Splitter::setOnSplitted(H264Splitter::onH264 cb) { @@ -43,7 +47,7 @@ ssize_t H264Splitter::onRecvHeader(const char *data, size_t len) { return 0; } -const char *H264Splitter::onSearchPacketTail(const char *data, size_t len) { +static const char *onSearchPacketTail_l(const char *data, size_t len) { for (size_t i = 2; len > 2 && i < len - 2; ++i) { //判断0x00 00 01 if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) { @@ -57,11 +61,33 @@ const char *H264Splitter::onSearchPacketTail(const char *data, size_t len) { return nullptr; } +const char *H264Splitter::onSearchPacketTail(const char *data, size_t len) { + auto last_frame = data + _search_pos; + auto next_frame = onSearchPacketTail_l(last_frame, len - _search_pos); + if (!next_frame) { + return nullptr; + } + + auto last_frame_len = next_frame - last_frame; + Frame::Ptr frame; + if (_h265) { + frame = std::make_shared((char *) last_frame, last_frame_len, 0, 0, prefixSize(last_frame, last_frame_len)); + } else { + frame = std::make_shared((char *) last_frame, last_frame_len, 0, 0, prefixSize(last_frame, last_frame_len)); + } + if (frame->decodeAble()) { + _search_pos = 0; + return next_frame; + } + _search_pos += last_frame_len; + return nullptr; +} + //////////////////////////////////////////////////////////////////////////////////////////////////////// -API_EXPORT mk_h264_splitter API_CALL mk_h264_splitter_create(on_mk_h264_splitter_frame cb, void *user_data) { +API_EXPORT mk_h264_splitter API_CALL mk_h264_splitter_create(on_mk_h264_splitter_frame cb, void *user_data, int is_h265) { assert(cb); - auto ptr = new H264Splitter(); + auto ptr = new H264Splitter(is_h265); ptr->setOnSplitted([cb, ptr, user_data](const char *data, size_t len) { cb(user_data, reinterpret_cast(ptr), data, len); }); diff --git a/api/tests/h264_media_server.c b/api/tests/h264_media_server.c index 4ca99ffd..f10c9b6b 100644 --- a/api/tests/h264_media_server.c +++ b/api/tests/h264_media_server.c @@ -36,9 +36,108 @@ static void on_h264_frame(void *user_data, mk_h264_splitter splitter, const char mk_frame_unref(frame); } + +//按照json转义规则转义webrtc answer sdp +static char *escape_string(const char *ptr){ + char *escaped = malloc(2 * strlen(ptr)); + char *ptr_escaped = escaped; + while (1) { + switch (*ptr) { + case '\r': { + *(ptr_escaped++) = '\\'; + *(ptr_escaped++) = 'r'; + break; + } + case '\n': { + *(ptr_escaped++) = '\\'; + *(ptr_escaped++) = 'n'; + break; + } + case '\t': { + *(ptr_escaped++) = '\\'; + *(ptr_escaped++) = 't'; + break; + } + + default: { + *(ptr_escaped++) = *ptr; + if (!*ptr) { + return escaped; + } + break; + } + } + ++ptr; + } +} + +static void on_mk_webrtc_get_answer_sdp_func(void *user_data, const char *answer, const char *err) { + const char *response_header[] = { "Content-Type", "application/json", "Access-Control-Allow-Origin", "*" , NULL}; + if (answer) { + answer = escape_string(answer); + } + size_t len = answer ? 2 * strlen(answer) : 1024; + char *response_content = (char *)malloc(len); + + if (answer) { + snprintf(response_content, len, + "{" + "\"sdp\":\"%s\"," + "\"type\":\"answer\"," + "\"code\":0" + "}", + answer); + } else { + snprintf(response_content, len, + "{" + "\"msg\":\"%s\"," + "\"code\":-1" + "}", + err); + } + + mk_http_response_invoker_do_string(user_data, 200, response_header, response_content); + mk_http_response_invoker_clone_release(user_data); + free(response_content); + if (answer) { + free((void*)answer); + } +} +/** + * 收到http api请求广播(包括GET/POST) + * @param parser http请求内容对象 + * @param invoker 执行该invoker返回http回复 + * @param consumed 置1则说明我们要处理该事件 + * @param sender http客户端相关信息 + */ +//测试url : http://127.0.0.1/api/test +void API_CALL on_mk_http_request(const mk_parser parser, + const mk_http_response_invoker invoker, + int *consumed, + const mk_sock_info sender) { + + const char *url = mk_parser_get_url(parser); + *consumed = 1; + + if (strcmp(url, "/index/api/webrtc") == 0) { + //拦截api: /index/api/webrtc + char rtc_url[1024]; + snprintf(rtc_url, sizeof(rtc_url), "rtc://%s/%s/%s?%s", mk_parser_get_header(parser, "Host"), + mk_parser_get_url_param(parser, "app"), mk_parser_get_url_param(parser, "stream"), + mk_parser_get_url_params(parser)); + + mk_webrtc_get_answer_sdp(mk_http_response_invoker_clone(invoker), on_mk_webrtc_get_answer_sdp_func, + mk_parser_get_url_param(parser, "type"), mk_parser_get_content(parser, NULL), rtc_url); + } else { + *consumed = 0; + return; + } +} + int main(int argc, char *argv[]) { + char *ini_path = mk_util_get_exe_dir("config.ini"); mk_config config = { - .ini = NULL, + .ini = ini_path, .ini_is_path = 1, .log_level = 0, .log_mask = LOG_CONSOLE, @@ -50,11 +149,29 @@ int main(int argc, char *argv[]) { .thread_num = 0 }; mk_env_init(&config); + mk_free(ini_path); + mk_http_server_start(80, 0); mk_rtsp_server_start(554, 0); mk_rtmp_server_start(1935, 0); + mk_rtc_server_start(atoi(mk_get_option("rtc.port"))); - signal(SIGINT, s_on_exit);// 设置退出信号 + mk_events events = { + .on_mk_media_changed = NULL, + .on_mk_media_publish = NULL, + .on_mk_media_play = NULL, + .on_mk_media_not_found = NULL, + .on_mk_media_no_reader = NULL, + .on_mk_http_request = on_mk_http_request, + .on_mk_http_access = NULL, + .on_mk_http_before_access = NULL, + .on_mk_rtsp_get_realm = NULL, + .on_mk_rtsp_auth = NULL, + .on_mk_record_mp4 = NULL, + .on_mk_shell_login = NULL, + .on_mk_flow_report = NULL + }; + mk_events_listen(&events); FILE *fp = fopen(argv[1], "rb"); if (!fp) { @@ -65,14 +182,15 @@ int main(int argc, char *argv[]) { mk_media media = mk_media_create("__defaultVhost__", "live", "test", 0, 0, 0); //h264的codec //mk_media_init_video(media, 0, 0, 0, 0, 2 * 104 * 1024); - codec_args v_args={0}; - mk_track v_track = mk_track_create(MKCodecH264,&v_args); - mk_media_init_track(media,v_track); + codec_args v_args = {0}; + mk_track v_track = mk_track_create(MKCodecH264, &v_args); + mk_media_init_track(media, v_track); mk_media_init_complete(media); mk_track_unref(v_track); //创建h264分帧器 - mk_h264_splitter splitter = mk_h264_splitter_create(on_h264_frame, media); + mk_h264_splitter splitter = mk_h264_splitter_create(on_h264_frame, media, 0); + signal(SIGINT, s_on_exit);// 设置退出信号 char buf[1024]; while (!exit_flag) {