From cb0e5c6c576ffb10574da17e9c1cdc194319da3f Mon Sep 17 00:00:00 2001 From: ziyue <1213642868@qq.com> Date: Wed, 25 May 2022 15:38:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=A8=E9=9D=A2=E6=9B=B4=E6=96=B0=E6=95=B4?= =?UTF-8?q?=E7=90=86c=20sdk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_common.h | 31 +++--- api/include/mk_frame.h | 123 +++++++++++++++++++++ api/include/mk_media.h | 44 ++++++-- api/include/mk_mediakit.h | 3 + api/include/mk_player.h | 81 ++------------ api/include/mk_proxyplayer.h | 2 +- api/include/mk_pusher.h | 2 +- api/include/mk_tcp.h | 47 ++++++-- api/include/mk_thread.h | 65 ++++++++++- api/include/mk_track.h | 135 +++++++++++++++++++++++ api/include/mk_transcode.h | 154 ++++++++++++++++++++++++++ api/include/mk_webrtc_api.h | 1 + api/source/mk_common.cpp | 1 + api/source/mk_frame.cpp | 176 ++++++++++++++++++++++++++++++ api/source/mk_media.cpp | 22 +++- api/source/mk_player.cpp | 137 +++--------------------- api/source/mk_tcp.cpp | 169 ++++++++++++++++++++++------- api/source/mk_thread.cpp | 57 ++++++++++ api/source/mk_track.cpp | 196 ++++++++++++++++++++++++++++++++++ api/source/mk_transcode.cpp | 126 ++++++++++++++++++++++ api/tests/CMakeLists.sample | 6 ++ api/tests/h264_media_server.c | 11 +- api/tests/httpclient.c | 61 +++++++++++ api/tests/player_opencv.c | 108 +++++++++++++++++++ api/tests/pusher.c | 85 ++++----------- api/tests/server.c | 26 +---- api/tests/websocket.c | 36 +++---- src/Extension/Frame.cpp | 7 ++ src/Extension/Frame.h | 47 ++++---- 29 files changed, 1543 insertions(+), 416 deletions(-) create mode 100644 api/include/mk_frame.h create mode 100644 api/include/mk_track.h create mode 100644 api/include/mk_transcode.h create mode 100644 api/source/mk_frame.cpp create mode 100644 api/source/mk_track.cpp create mode 100644 api/source/mk_transcode.cpp create mode 100644 api/tests/CMakeLists.sample create mode 100644 api/tests/httpclient.c create mode 100644 api/tests/player_opencv.c diff --git a/api/include/mk_common.h b/api/include/mk_common.h index f9193033..63835410 100755 --- a/api/include/mk_common.h +++ b/api/include/mk_common.h @@ -14,24 +14,21 @@ #include #include -#if defined(_WIN32) - -#ifndef MediaKitApi_STATIC -#if defined(MediaKitApi_EXPORTS) - #define API_EXPORT __declspec(dllexport) - #else - #define API_EXPORT __declspec(dllimport) - #endif - - #define API_CALL __cdecl +#if defined(_WIN32) && defined(_MSC_VER) +# ifndef MediaKitApi_STATIC +# if defined(MediaKitApi_EXPORTS) +# define API_EXPORT __declspec(dllexport) +# else +# define API_EXPORT __declspec(dllimport) +# endif +# define API_CALL __cdecl +# else +# define API_EXPORT +# define API_CALL +# endif #else -#define API_EXPORT -#define API_CALL -#endif - -#else -#define API_EXPORT -#define API_CALL +# define API_EXPORT +# define API_CALL #endif #ifdef __cplusplus diff --git a/api/include/mk_frame.h b/api/include/mk_frame.h new file mode 100644 index 00000000..e7a7c7f0 --- /dev/null +++ b/api/include/mk_frame.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_MK_FRAME_H +#define ZLMEDIAKIT_MK_FRAME_H + +#include "mk_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//是否为关键帧 +#define MK_FRAME_FLAG_IS_KEY (1 << 0) +//是否为配置帧(sps/pps/vps等) +#define MK_FRAME_FLAG_IS_CONFIG (1 << 1) +//是否可丢弃的帧(sei/aud) +#define MK_FRAME_FLAG_DROP_ABLE (1 << 2) +//是否不可单独解码的帧(多slice的非vcl帧) +#define MK_FRAME_FLAG_NOT_DECODE_ABLE (1 << 3) + +//codec id常量定义 +API_EXPORT extern const int MKCodecH264; +API_EXPORT extern const int MKCodecH265; +API_EXPORT extern const int MKCodecAAC; +API_EXPORT extern const int MKCodecG711A; +API_EXPORT extern const int MKCodecG711U; +API_EXPORT extern const int MKCodecOpus; +API_EXPORT extern const int MKCodecL16; +API_EXPORT extern const int MKCodecVP8; +API_EXPORT extern const int MKCodecVP9; +API_EXPORT extern const int MKCodecAV1; +API_EXPORT extern const int MKCodecJPEG; + +typedef void *mk_frame; + +// 用户自定义free回调函数 +typedef void(API_CALL *on_mk_frame_data_release)(void *user_data, char *ptr); + +/** + * 创建frame对象,并返回其引用 + * @param codec_id 编解码类型,请参考MKCodecXXX定义 + * @param dts 解码时间戳,单位毫秒 + * @param pts 显示时间戳,单位毫秒 + * @param data 单帧数据 + * @param size 单帧数据长度 + * @param cb data指针free释放回调, 如果为空,内部会拷贝数据 + * @param user_data data指针free释放回调用户指针 + * @return frame对象引用 + */ +API_EXPORT mk_frame API_CALL mk_frame_create(int codec_id, uint32_t dts, uint32_t pts, const char *data, size_t size, + on_mk_frame_data_release cb, void *user_data); + +/** + * 减引用frame对象 + * @param frame 帧对象引用 + */ +API_EXPORT void API_CALL mk_frame_unref(mk_frame frame); + +/** + * 引用frame对象 + * @param frame 被引用的frame对象 + * @return 新的对象引用 + */ +API_EXPORT mk_frame API_CALL mk_frame_ref(mk_frame frame); + +/** + * 获取frame 编码codec类型,请参考MKCodecXXX定义 + */ +API_EXPORT int API_CALL mk_frame_codec_id(mk_frame frame); + +/** + * 获取帧编码codec名称 + */ +API_EXPORT const char* API_CALL mk_frame_codec_name(mk_frame frame); + +/** + * 帧是否为视频 + */ +API_EXPORT int API_CALL mk_frame_is_video(mk_frame frame); + +/** + * 获取帧数据指针 + */ +API_EXPORT const char* API_CALL mk_frame_get_data(mk_frame frame); + +/** + * 获取帧数据指针长度 + */ +API_EXPORT size_t API_CALL mk_frame_get_data_size(mk_frame frame); + +/** + * 返回帧数据前缀长度,譬如H264/H265前缀一般是0x00 00 00 01,那么本函数返回4 + */ +API_EXPORT size_t API_CALL mk_frame_get_data_prefix_size(mk_frame frame); + +/** + * 获取解码时间戳,单位毫秒 + */ +API_EXPORT uint32_t API_CALL mk_frame_get_dts(mk_frame frame); + +/** + * 获取显示时间戳,单位毫秒 + */ +API_EXPORT uint32_t API_CALL mk_frame_get_pts(mk_frame frame); + +/** + * 获取帧flag,请参考 MK_FRAME_FLAG + */ +API_EXPORT uint32_t API_CALL mk_frame_get_flags(mk_frame frame); + +#ifdef __cplusplus +} +#endif + +#endif //ZLMEDIAKIT_MK_FRAME_H diff --git a/api/include/mk_media.h b/api/include/mk_media.h index 57b3e187..a93ededc 100755 --- a/api/include/mk_media.h +++ b/api/include/mk_media.h @@ -12,6 +12,8 @@ #define MK_MEDIA_H_ #include "mk_common.h" +#include "mk_track.h" +#include "mk_frame.h" #include "mk_events_objects.h" #ifdef __cplusplus @@ -40,18 +42,29 @@ API_EXPORT mk_media API_CALL mk_media_create(const char *vhost, const char *app, API_EXPORT void API_CALL mk_media_release(mk_media ctx); /** - * 添加视频轨道 + * 添加音视频track + * @param ctx mk_media对象 + * @param track mk_track对象,音视频轨道 + */ +API_EXPORT void API_CALL mk_media_init_track(mk_media ctx, mk_track track); + +/** + * 添加视频轨道,请改用mk_media_init_track方法 * @param ctx 对象指针 * @param codec_id 0:CodecH264/1:CodecH265 + * @param width 视频宽度; 在编码时才有效 + * @param height 视频高度; 在编码时才有效 + * @param fps 视频fps; 在编码时才有效 + * @param bit_rate 视频比特率,单位bps; 在编码时才有效 * @param width 视频宽度 * @param height 视频高度 * @param fps 视频fps * @return 1代表成功,0失败 */ -API_EXPORT int API_CALL mk_media_init_video(mk_media ctx, int codec_id, int width, int height, float fps); +API_EXPORT int API_CALL mk_media_init_video(mk_media ctx, int codec_id, int width, int height, float fps, int bit_rate); /** - * 添加音频轨道 + * 添加音频轨道,请改用mk_media_init_track方法 * @param ctx 对象指针 * @param codec_id 2:CodecAAC/3:CodecG711A/4:CodecG711U/5:OPUS * @param channel 通道数 @@ -70,7 +83,15 @@ API_EXPORT int API_CALL mk_media_init_audio(mk_media ctx, int codec_id, int samp API_EXPORT void API_CALL mk_media_init_complete(mk_media ctx); /** - * 输入单帧H264视频,帧起始字节00 00 01,00 00 00 01均可 + * 输入frame对象 + * @param ctx mk_media对象 + * @param frame 帧对象 + * @return 1代表成功,0失败 + */ +API_EXPORT int API_CALL mk_media_input_frame(mk_media ctx, mk_frame frame); + +/** + * 输入单帧H264视频,帧起始字节00 00 01,00 00 00 01均可,请改用mk_media_input_frame方法 * @param ctx 对象指针 * @param data 单帧H264数据 * @param len 单帧H264数据字节数 @@ -81,7 +102,7 @@ API_EXPORT void API_CALL mk_media_init_complete(mk_media ctx); API_EXPORT int API_CALL mk_media_input_h264(mk_media ctx, const void *data, int len, uint32_t dts, uint32_t pts); /** - * 输入单帧H265视频,帧起始字节00 00 01,00 00 00 01均可 + * 输入单帧H265视频,帧起始字节00 00 01,00 00 00 01均可,请改用mk_media_input_frame方法 * @param ctx 对象指针 * @param data 单帧H265数据 * @param len 单帧H265数据字节数 @@ -92,7 +113,16 @@ API_EXPORT int API_CALL mk_media_input_h264(mk_media ctx, const void *data, int API_EXPORT int API_CALL mk_media_input_h265(mk_media ctx, const void *data, int len, uint32_t dts, uint32_t pts); /** - * 输入单帧AAC音频(单独指定adts头) + * 输入YUV视频数据 + * @param ctx 对象指针 + * @param yuv yuv420p数据 + * @param linesize yuv420p linesize + * @param cts 视频采集时间戳,单位毫秒 + */ +API_EXPORT void API_CALL mk_media_input_yuv(mk_media ctx, const char *yuv[3], int linesize[3], uint32_t cts); + +/** + * 输入单帧AAC音频(单独指定adts头),请改用mk_media_input_frame方法 * @param ctx 对象指针 * @param data 不包含adts头的单帧AAC数据,adts头7个字节 * @param len 单帧AAC数据字节数 @@ -113,7 +143,7 @@ API_EXPORT int API_CALL mk_media_input_aac(mk_media ctx, const void *data, int l API_EXPORT int API_CALL mk_media_input_pcm(mk_media ctx, void *data, int len, uint32_t pts); /** - * 输入单帧OPUS/G711音频帧 + * 输入单帧OPUS/G711音频帧,请改用mk_media_input_frame方法 * @param ctx 对象指针 * @param data 单帧音频数据 * @param len 单帧音频数据字节数 diff --git a/api/include/mk_mediakit.h b/api/include/mk_mediakit.h index e42e23c1..42c1ddc0 100755 --- a/api/include/mk_mediakit.h +++ b/api/include/mk_mediakit.h @@ -24,5 +24,8 @@ #include "mk_thread.h" #include "mk_rtp_server.h" #include "mk_h264_splitter.h" +#include "mk_frame.h" +#include "mk_track.h" +#include "mk_transcode.h" #endif /* MK_API_H_ */ diff --git a/api/include/mk_player.h b/api/include/mk_player.h index e0e8c83a..c74d9739 100755 --- a/api/include/mk_player.h +++ b/api/include/mk_player.h @@ -12,6 +12,8 @@ #define MK_PLAYER_H_ #include "mk_common.h" +#include "mk_frame.h" +#include "mk_track.h" #ifdef __cplusplus extern "C" { @@ -24,20 +26,11 @@ typedef void* mk_player; * @param user_data 用户数据指针 * @param err_code 错误代码,0为成功 * @param err_msg 错误提示 + * @param tracks track列表 + * @param track_count track个数 */ -typedef void(API_CALL *on_mk_play_event)(void *user_data,int err_code,const char *err_msg); - -/** - * 收到音视频数据回调 - * @param user_data 用户数据指针 - * @param track_type 0:视频,1:音频 - * @param codec_id 0:H264,1:H265,2:AAC 3.G711A 4.G711U 5.OPUS - * @param data 数据指针 - * @param len 数据长度 - * @param dts 解码时间戳,单位毫秒 - * @param pts 显示时间戳,单位毫秒 - */ -typedef void(API_CALL *on_mk_play_data)(void *user_data,int track_type,int codec_id,void *data,size_t len, uint32_t dts,uint32_t pts); +typedef void(API_CALL *on_mk_play_event)(void *user_data, int err_code, const char *err_msg, mk_track tracks[], + int track_count); /** * 创建一个播放器,支持rtmp[s]/rtsp[s] @@ -54,7 +47,7 @@ API_EXPORT void API_CALL mk_player_release(mk_player ctx); /** * 设置播放器配置选项 * @param ctx 播放器指针 - * @param key 配置项键,支持 net_adapter/rtp_type/rtsp_user/rtsp_pwd/protocol_timeout_ms/media_timeout_ms/beat_interval_ms/max_analysis_ms + * @param key 配置项键,支持 net_adapter/rtp_type/rtsp_user/rtsp_pwd/protocol_timeout_ms/media_timeout_ms/beat_interval_ms/wait_track_ready * @param val 配置项值,如果是整形,需要转换成统一转换成string */ API_EXPORT void API_CALL mk_player_set_option(mk_player ctx, const char *key, const char *val); @@ -110,67 +103,8 @@ API_EXPORT void API_CALL mk_player_set_on_result(mk_player ctx, on_mk_play_event */ API_EXPORT void API_CALL mk_player_set_on_shutdown(mk_player ctx, on_mk_play_event cb, void *user_data); -/** - * 设置音视频数据回调函数 - * @param ctx 播放器指针 - * @param cb 回调函数指针,设置null立即取消回调 - * @param user_data 用户数据指针 - */ -API_EXPORT void API_CALL mk_player_set_on_data(mk_player ctx, on_mk_play_data cb, void *user_data); - ///////////////////////////获取音视频相关信息接口在播放成功回调触发后才有效/////////////////////////////// -/** - * 获取视频codec_id -1:不存在 0:H264,1:H265,2:AAC 3.G711A 4.G711U - * @param ctx 播放器指针 - */ -API_EXPORT int API_CALL mk_player_video_codec_id(mk_player ctx); - -/** - * 获取视频codec_id, vendor类型, 私有头数据 codec_id -1:不存在 0:H264,1:H265,2:AAC 3.G711A 4.G711U - * @param ctx 播放器指针 - * @param vendor 输出厂家类型 如果是私有流 应该是H264另外还有厂家类型 - * @param head 厂家SDK头数据 - * @param head 厂家SDK头数据长度 - */ -API_EXPORT int API_CALL mk_player_video_codec_id_vendor_head(mk_player ctx, char* vendor, char* head, int* headLen); - -/** - * 获取视频宽度 - */ -API_EXPORT int API_CALL mk_player_video_width(mk_player ctx); - -/** - * 获取视频高度 - */ -API_EXPORT int API_CALL mk_player_video_height(mk_player ctx); - -/** - * 获取视频帧率 - */ -API_EXPORT float API_CALL mk_player_video_fps(mk_player ctx); - -/** - * 获取音频codec_id -1:不存在 0:H264,1:H265,2:AAC 3.G711A 4.G711U - * @param ctx 播放器指针 - */ -API_EXPORT int API_CALL mk_player_audio_codec_id(mk_player ctx); - -/** - * 获取音频采样率 - */ -API_EXPORT int API_CALL mk_player_audio_samplerate(mk_player ctx); - -/** - * 获取音频采样位数,一般为16 - */ -API_EXPORT int API_CALL mk_player_audio_bit(mk_player ctx); - -/** - * 获取音频通道数 - */ -API_EXPORT int API_CALL mk_player_audio_channel(mk_player ctx); - /** * 获取点播节目时长,如果是直播返回0,否则返回秒数 */ @@ -193,7 +127,6 @@ API_EXPORT int API_CALL mk_player_progress_pos(mk_player ctx); */ API_EXPORT float API_CALL mk_player_loss_rate(mk_player ctx, int track_type); - #ifdef __cplusplus } #endif diff --git a/api/include/mk_proxyplayer.h b/api/include/mk_proxyplayer.h index 0dcc0923..53736397 100644 --- a/api/include/mk_proxyplayer.h +++ b/api/include/mk_proxyplayer.h @@ -40,7 +40,7 @@ API_EXPORT void API_CALL mk_proxy_player_release(mk_proxy_player ctx); /** * 设置代理播放器配置选项 * @param ctx 代理播放器指针 - * @param key 配置项键,支持 net_adapter/rtp_type/rtsp_user/rtsp_pwd/protocol_timeout_ms/media_timeout_ms/beat_interval_ms/max_analysis_ms + * @param key 配置项键,支持 net_adapter/rtp_type/rtsp_user/rtsp_pwd/protocol_timeout_ms/media_timeout_ms/beat_interval_ms * @param val 配置项值,如果是整形,需要转换成统一转换成string */ API_EXPORT void API_CALL mk_proxy_player_set_option(mk_proxy_player ctx, const char *key, const char *val); diff --git a/api/include/mk_pusher.h b/api/include/mk_pusher.h index ab94350b..2f2883ef 100644 --- a/api/include/mk_pusher.h +++ b/api/include/mk_pusher.h @@ -60,7 +60,7 @@ API_EXPORT void API_CALL mk_pusher_release(mk_pusher ctx); /** * 设置推流器配置选项 * @param ctx 推流器指针 - * @param key 配置项键,支持 net_adapter/rtp_type/rtsp_user/rtsp_pwd/protocol_timeout_ms/media_timeout_ms/beat_interval_ms/max_analysis_ms + * @param key 配置项键,支持 net_adapter/rtp_type/rtsp_user/rtsp_pwd/protocol_timeout_ms/media_timeout_ms/beat_interval_ms * @param val 配置项值,如果是整形,需要转换成统一转换成string */ API_EXPORT void API_CALL mk_pusher_set_option(mk_pusher ctx, const char *key, const char *val); diff --git a/api/include/mk_tcp.h b/api/include/mk_tcp.h index 6e40e27e..910522f9 100644 --- a/api/include/mk_tcp.h +++ b/api/include/mk_tcp.h @@ -17,6 +17,25 @@ extern "C" { #endif +///////////////////////////////////////////Buffer::Ptr///////////////////////////////////////////// + +typedef void *mk_buffer; +typedef void(API_CALL *on_mk_buffer_free)(void *user_data, void *data); + +/** + * 创建buffer对象 + * @param data 数据指针 + * @param len 数据长度 + * @param cb 数据指针free回调函数,该参数置空时,内部会拷贝数据 + * @param user_data 数据指针free回调函数on_mk_buffer_free第一个参数 + * @return buffer对象 + */ +API_EXPORT mk_buffer API_CALL mk_buffer_from_char(const char *data, size_t len, on_mk_buffer_free cb, void *user_data); +API_EXPORT mk_buffer API_CALL mk_buffer_ref(mk_buffer buffer); +API_EXPORT void API_CALL mk_buffer_unref(mk_buffer buffer); +API_EXPORT const char* API_CALL mk_buffer_get_data(mk_buffer buffer); +API_EXPORT size_t API_CALL mk_buffer_get_size(mk_buffer buffer); + ///////////////////////////////////////////SockInfo///////////////////////////////////////////// //SockInfo对象的C映射 typedef void* mk_sock_info; @@ -47,15 +66,27 @@ API_EXPORT uint16_t API_CALL mk_sock_info_local_port(const mk_sock_info ctx); ///////////////////////////////////////////TcpSession///////////////////////////////////////////// //TcpSession对象的C映射 typedef void* mk_tcp_session; +typedef void* mk_tcp_session_ref; + //获取基类指针以便获取其网络相关信息 API_EXPORT mk_sock_info API_CALL mk_tcp_session_get_sock_info(const mk_tcp_session ctx); //TcpSession::safeShutdown() API_EXPORT void API_CALL mk_tcp_session_shutdown(const mk_tcp_session ctx,int err,const char *err_msg); //TcpSession::send() -API_EXPORT void API_CALL mk_tcp_session_send(const mk_tcp_session ctx,const char *data,size_t len); +API_EXPORT void API_CALL mk_tcp_session_send(const mk_tcp_session ctx, const char *data, size_t len); +API_EXPORT void API_CALL mk_tcp_session_send_buffer(const mk_tcp_session ctx, mk_buffer buffer); + //切换到该对象所在线程后再TcpSession::send() -API_EXPORT void API_CALL mk_tcp_session_send_safe(const mk_tcp_session ctx,const char *data,size_t len); +API_EXPORT void API_CALL mk_tcp_session_send_safe(const mk_tcp_session ctx, const char *data, size_t len); +API_EXPORT void API_CALL mk_tcp_session_send_buffer_safe(const mk_tcp_session ctx, mk_buffer buffer); + +//创建mk_tcp_session的弱引用 +API_EXPORT mk_tcp_session_ref API_CALL mk_tcp_session_ref_from(const mk_tcp_session ctx); +//删除mk_tcp_session的弱引用 +API_EXPORT void mk_tcp_session_ref_release(const mk_tcp_session_ref ref); +//根据弱引用获取mk_tcp_session,如果mk_tcp_session已经销毁,那么返回NULL +API_EXPORT mk_tcp_session mk_tcp_session_from_ref(const mk_tcp_session_ref ref); ///////////////////////////////////////////自定义tcp服务///////////////////////////////////////////// @@ -71,10 +102,9 @@ typedef struct { * 收到客户端发过来的数据 * @param server_port 服务器端口号 * @param session 会话处理对象 - * @param data 数据指针 - * @param len 数据长度 + * @param buffer 数据 */ - void (API_CALL *on_mk_tcp_session_data)(uint16_t server_port,mk_tcp_session session,const char *data,size_t len); + void (API_CALL *on_mk_tcp_session_data)(uint16_t server_port,mk_tcp_session session, mk_buffer buffer); /** * 每隔2秒的定时器,用于管理超时等任务 @@ -161,10 +191,9 @@ typedef struct { /** * 收到tcp服务器发来的数据 * @param client tcp客户端 - * @param data 数据指针 - * @param len 数据长度 + * @param buffer 数据 */ - void (API_CALL *on_mk_tcp_client_data)(mk_tcp_client client,const char *data,size_t len); + void (API_CALL *on_mk_tcp_client_data)(mk_tcp_client client, mk_buffer buffer); /** * 每隔2秒的定时器,用于管理超时等任务 @@ -205,6 +234,7 @@ API_EXPORT void API_CALL mk_tcp_client_connect(mk_tcp_client ctx, const char *ho * @param len 数据长度,等于0时,内部通过strlen获取 */ API_EXPORT void API_CALL mk_tcp_client_send(mk_tcp_client ctx, const char *data, int len); +API_EXPORT void API_CALL mk_tcp_client_send_buffer(mk_tcp_client ctx, mk_buffer buffer); /** * 切换到本对象的网络线程后再发送数据 @@ -213,6 +243,7 @@ API_EXPORT void API_CALL mk_tcp_client_send(mk_tcp_client ctx, const char *data, * @param len 数据长度,等于0时,内部通过strlen获取 */ API_EXPORT void API_CALL mk_tcp_client_send_safe(mk_tcp_client ctx, const char *data, int len); +API_EXPORT void API_CALL mk_tcp_client_send_buffer_safe(mk_tcp_client ctx, mk_buffer buffer); /** * 客户端附着用户数据 diff --git a/api/include/mk_thread.h b/api/include/mk_thread.h index 0426d711..dfd80edf 100644 --- a/api/include/mk_thread.h +++ b/api/include/mk_thread.h @@ -52,6 +52,31 @@ API_EXPORT mk_thread API_CALL mk_thread_from_pool(); */ API_EXPORT mk_thread API_CALL mk_thread_from_pool_work(); +typedef void* mk_thread_pool; + +/** + * 创建线程池 + * @param name 线程池名称,方便调试 + * @param n_thread 线程个数,0时为cpu个数 + * @param priority 线程优先级,分为PRIORITY_LOWEST = 0,PRIORITY_LOW, PRIORITY_NORMAL, PRIORITY_HIGH, PRIORITY_HIGHEST + * @return 线程池 + */ +API_EXPORT mk_thread_pool API_CALL mk_thread_pool_create(const char *name, size_t n_thread, int priority); + +/** + * 销毁线程池 + * @param pool 线程池 + * @return 0:成功 + */ +API_EXPORT int API_CALL mk_thread_pool_release(mk_thread_pool pool); + +/** + * 从线程池获取一个线程 + * @param pool 线程池 + * @return 线程 + */ +API_EXPORT mk_thread API_CALL mk_thread_from_thread_pool(mk_thread_pool pool); + ///////////////////////////////////////////线程切换///////////////////////////////////////////// typedef void (API_CALL *on_mk_async)(void *user_data); @@ -61,7 +86,16 @@ typedef void (API_CALL *on_mk_async)(void *user_data); * @param cb 回调函数 * @param user_data 用户数据指针 */ -API_EXPORT void API_CALL mk_async_do(mk_thread ctx,on_mk_async cb, void *user_data); +API_EXPORT void API_CALL mk_async_do(mk_thread ctx, on_mk_async cb, void *user_data); + +/** + * 切换到事件线程并延时执行 + * @param ctx 事件线程 + * @param ms 延时时间,单位毫秒 + * @param cb 回调函数 + * @param user_data 用户数据指针 + */ +API_EXPORT void API_CALL mk_async_do_delay(mk_thread ctx, size_t ms, on_mk_async cb, void *user_data); /** * 切换到事件线程并同步执行 @@ -69,7 +103,7 @@ API_EXPORT void API_CALL mk_async_do(mk_thread ctx,on_mk_async cb, void *user_da * @param cb 回调函数 * @param user_data 用户数据指针 */ -API_EXPORT void API_CALL mk_sync_do(mk_thread ctx,on_mk_async cb, void *user_data); +API_EXPORT void API_CALL mk_sync_do(mk_thread ctx, on_mk_async cb, void *user_data); ///////////////////////////////////////////定时器///////////////////////////////////////////// typedef void* mk_timer; @@ -88,7 +122,7 @@ typedef uint64_t (API_CALL *on_mk_timer)(void *user_data); * @param user_data 用户数据指针 * @return 定时器对象 */ -API_EXPORT mk_timer API_CALL mk_timer_create(mk_thread ctx,uint64_t delay_ms, on_mk_timer cb, void *user_data); +API_EXPORT mk_timer API_CALL mk_timer_create(mk_thread ctx, uint64_t delay_ms, on_mk_timer cb, void *user_data); /** * 销毁和取消定时器 @@ -96,6 +130,31 @@ API_EXPORT mk_timer API_CALL mk_timer_create(mk_thread ctx,uint64_t delay_ms, on */ API_EXPORT void API_CALL mk_timer_release(mk_timer ctx); +///////////////////////////////////////////信号量///////////////////////////////////////////// + +typedef void* mk_sem; + +/** + * 创建信号量 + */ +API_EXPORT mk_sem API_CALL mk_sem_create(); + +/** + * 销毁信号量 + */ +API_EXPORT void API_CALL mk_sem_release(mk_sem sem); + +/** + * 信号量加n + */ +API_EXPORT void API_CALL mk_sem_post(mk_sem sem, size_t n); + +/** + * 信号量减1 + * @param sem + */ +API_EXPORT void API_CALL mk_sem_wait(mk_sem sem); + #ifdef __cplusplus } #endif diff --git a/api/include/mk_track.h b/api/include/mk_track.h new file mode 100644 index 00000000..e1d88bf6 --- /dev/null +++ b/api/include/mk_track.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_MK_TRACK_H +#define ZLMEDIAKIT_MK_TRACK_H + +#include "mk_common.h" +#include "mk_frame.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//音视频轨道 +typedef void* mk_track; +//输出frame回调 +typedef void(API_CALL *on_mk_frame_out)(void *user_data, mk_frame frame); + +//track创建参数 +typedef union { + struct { + int width; + int height; + int fps; + } video; + + struct { + int channels; + int sample_rate; + } audio; +} codec_args; + +/** + * 创建track对象引用 + * @param codec_id 请参考MKCodecXXX 常量定义 + * @param args 视频或音频参数 + * @return track对象引用 + */ +API_EXPORT mk_track API_CALL mk_track_create(int codec_id, codec_args *args); + +/** + * 减引用track对象 + * @param track track对象 + */ +API_EXPORT void API_CALL mk_track_unref(mk_track track); + +/** + * 引用track对象 + * @param track track对象 + * @return 新的track引用对象 + */ +API_EXPORT mk_track API_CALL mk_track_ref(mk_track track); + +/** + * 获取track 编码codec类型,请参考MKCodecXXX定义 + */ +API_EXPORT int API_CALL mk_track_codec_id(mk_track track); + +/** + * 获取编码codec名称 + */ +API_EXPORT const char* API_CALL mk_track_codec_name(mk_track track); + +/** + * 获取比特率信息 + */ +API_EXPORT int API_CALL mk_track_bit_rate(mk_track track); + +/** + * 监听frame输出事件 + * @param track track对象 + * @param cb frame输出回调 + * @param user_data frame输出回调用户指针参数 + */ +API_EXPORT void *API_CALL mk_track_add_delegate(mk_track track, on_mk_frame_out cb, void *user_data); + +/** + * 取消frame输出事件监听 + * @param track track对象 + * @param tag mk_track_add_delegate返回值 + */ +API_EXPORT void API_CALL mk_track_del_delegate(mk_track track, void *tag); + +/** + * 输入frame到track,通常你不需要调用此api + */ +API_EXPORT void API_CALL mk_track_input_frame(mk_track track, mk_frame frame); + +/** + * track是否为视频 + */ +API_EXPORT int API_CALL mk_track_is_video(mk_track track); + +/** + * 获取视频宽度 + */ +API_EXPORT int API_CALL mk_track_video_width(mk_track track); + +/** + * 获取视频高度 + */ +API_EXPORT int API_CALL mk_track_video_height(mk_track track); + +/** + * 获取视频帧率 + */ +API_EXPORT int API_CALL mk_track_video_fps(mk_track track); + +/** + * 获取音频采样率 + */ +API_EXPORT int API_CALL mk_track_audio_sample_rate(mk_track track); + +/** + * 获取音频通道数 + */ +API_EXPORT int API_CALL mk_track_audio_channel(mk_track track); + +/** + * 获取音频位数,一般为16bit + */ +API_EXPORT int API_CALL mk_track_audio_sample_bit(mk_track track); + +#ifdef __cplusplus +} +#endif + +#endif //ZLMEDIAKIT_MK_TRACK_H \ No newline at end of file diff --git a/api/include/mk_transcode.h b/api/include/mk_transcode.h new file mode 100644 index 00000000..2fa71cae --- /dev/null +++ b/api/include/mk_transcode.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_MK_TRANSCODE_H +#define ZLMEDIAKIT_MK_TRANSCODE_H + +#include "mk_common.h" +#include "mk_track.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//解码器对象 +typedef void *mk_decoder; +//解码后的frame +typedef void *mk_frame_pix; +//SwsContext的包装 +typedef void *mk_swscale; +//FFmpeg原始解码帧对象 +typedef struct AVFrame AVFrame; +//FFmpeg编解码器对象 +typedef struct AVCodecContext AVCodecContext; +//解码输出回调 +typedef void(API_CALL *on_mk_decode)(void *user_data, mk_frame_pix frame); + +/** + * 创建解码器 + * @param track track对象 + * @param thread_num 解码线程数,0时为自动 + * @return 返回解码器对象,NULL代表失败 + */ +API_EXPORT mk_decoder API_CALL mk_decoder_create(mk_track track, int thread_num); + +/** + * 销毁解码器 + * @param ctx 解码器对象 + * @param flush_frame 是否等待所有帧解码成功 + */ +API_EXPORT void API_CALL mk_decoder_release(mk_decoder ctx, int flush_frame); + +/** + * 解码音视频帧 + * @param ctx 解码器 + * @param frame 帧对象 + * @param async 是否异步解码 + * @param enable_merge 是否合并帧解码,有些情况下,需要把时间戳相同的slice合并输入到解码器才能解码 + */ +API_EXPORT void API_CALL mk_decoder_decode(mk_decoder ctx, mk_frame frame, int async, int enable_merge); + +/** + * 设置异步解码最大帧缓存积压数限制 + */ +API_EXPORT void API_CALL mk_decoder_set_max_async_frame_size(mk_decoder ctx, size_t size); + +/** + * 设置解码输出回调 + * @param ctx 解码器 + * @param cb 回调函数 + * @param user_data 回调函数用户指针参数 + */ +API_EXPORT void API_CALL mk_decoder_set_cb(mk_decoder ctx, on_mk_decode cb, void *user_data); + +/** + * 获取FFmpeg原始AVCodecContext对象 + * @param ctx 解码器 + */ +API_EXPORT const AVCodecContext* API_CALL mk_decoder_get_context(mk_decoder ctx); + +///////////////////////////////////////////////////////////////////////////////////////////// + +/** + * 创建解码帧mk_frame_pix新引用 + * @param frame 原始引用 + * @return 新引用 + */ +API_EXPORT mk_frame_pix API_CALL mk_frame_pix_ref(mk_frame_pix frame); + +/** + * 解码帧mk_frame_pix减引用 + * @param frame 原始引用 + */ +API_EXPORT void API_CALL mk_frame_pix_unref(mk_frame_pix frame); + +/** + * 从FFmpeg AVFrame转换为mk_frame_pix + * @param frame FFmpeg AVFrame + * @return mk_frame_pix对象 + */ +API_EXPORT mk_frame_pix API_CALL mk_frame_pix_from_av_frame(AVFrame *frame); + +/** + * 获取FFmpeg AVFrame对象 + * @param frame 解码帧mk_frame_pix + * @return FFmpeg AVFrame对象 + */ +API_EXPORT AVFrame* API_CALL mk_frame_pix_get_av_frame(mk_frame_pix frame); + +///////////////////////////////////////////////////////////////////////////////////////////// + +/** + * 创建ffmpeg SwsContext wrapper实例 + * @param output AVPixelFormat类型,AV_PIX_FMT_BGR24==3 + * @param width 目标宽度,置0时,则与输入时一致 + * @param height 目标高度,置0时,则与输入时一致 + * @return SwsContext wrapper 实例 + */ +API_EXPORT mk_swscale mk_swscale_create(int output, int width, int height); + +/** + * 释放ffmpeg SwsContext wrapper实例 + * @param ctx SwsContext wrapper实例 + */ +API_EXPORT void mk_swscale_release(mk_swscale ctx); + +/** + * 使用SwsContext转换pix format + * @param ctx SwsContext wrapper实例 + * @param frame pix frame + * @param out 转换后存放的数据指针,用户需要确保提前申请并大小足够 + * @return sws_scale()返回值:the height of the output slice + */ +API_EXPORT int mk_swscale_input_frame(mk_swscale ctx, mk_frame_pix frame, uint8_t *out); + +/** + * 使用SwsContext转换pix format + * @param ctx SwsContext wrapper实例 + * @param frame pix frame + * @return 新的pix frame对象,需要使用mk_frame_pix_unref销毁 + */ +API_EXPORT mk_frame_pix mk_swscale_input_frame2(mk_swscale ctx, mk_frame_pix frame); + +///////////////////////////////////////////////////////////////////////////////////////////// + +API_EXPORT uint8_t** API_CALL mk_get_av_frame_data(AVFrame *frame); +API_EXPORT int* API_CALL mk_get_av_frame_line_size(AVFrame *frame); +API_EXPORT int64_t API_CALL mk_get_av_frame_dts(AVFrame *frame); +API_EXPORT int64_t API_CALL mk_get_av_frame_pts(AVFrame *frame); +API_EXPORT int API_CALL mk_get_av_frame_width(AVFrame *frame); +API_EXPORT int API_CALL mk_get_av_frame_height(AVFrame *frame); +API_EXPORT int API_CALL mk_get_av_frame_format(AVFrame *frame); + +#ifdef __cplusplus +} +#endif + +#endif //ZLMEDIAKIT_MK_TRANSCODE_H \ No newline at end of file diff --git a/api/include/mk_webrtc_api.h b/api/include/mk_webrtc_api.h index 0151c74b..b4b22cc8 100644 --- a/api/include/mk_webrtc_api.h +++ b/api/include/mk_webrtc_api.h @@ -7,6 +7,7 @@ * LICENSE file in the root of the source tree. All contributing project authors * may be found in the AUTHORS file in the root of the source tree. */ + #ifdef ENABLE_WEBRTC #ifndef MK_WEBRTC_API_H #define MK_WEBRTC_API_H diff --git a/api/source/mk_common.cpp b/api/source/mk_common.cpp index 1c821da8..09a0d4fa 100644 --- a/api/source/mk_common.cpp +++ b/api/source/mk_common.cpp @@ -14,6 +14,7 @@ #include "Util/logger.h" #include "Util/SSLBox.h" #include "Network/TcpServer.h" +#include "Network/UdpServer.h" #include "Thread/WorkThreadPool.h" #include "Rtsp/RtspSession.h" diff --git a/api/source/mk_frame.cpp b/api/source/mk_frame.cpp new file mode 100644 index 00000000..eb870887 --- /dev/null +++ b/api/source/mk_frame.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "mk_frame.h" +#include "Extension/Frame.h" +#include "Extension/H264.h" +#include "Extension/H265.h" +#include "Extension/AAC.h" + +using namespace mediakit; + +extern "C" { +#define XX(name, type, value, str, mpeg_id) const int MK##name = value; + CODEC_MAP(XX) +#undef XX +} + +class FrameFromPtrForC : public FrameFromPtr { +public: + using Ptr = std::shared_ptr; + + template + FrameFromPtrForC(bool cache_able, uint32_t flags, on_mk_frame_data_release cb, void *user_data, ARGS &&...args) : FrameFromPtr( + std::forward(args)...) { + _flags = flags; + _cb = cb; + _user_data = user_data; + _cache_able = cache_able; + } + + ~FrameFromPtrForC() override { + if (_cb) { + _cb(_user_data, _ptr); + } + } + + bool cacheAble() const override { + return _cache_able; + } + + bool keyFrame() const override { + return _flags & MK_FRAME_FLAG_IS_KEY; + } + + bool configFrame() const override { + return _flags & MK_FRAME_FLAG_IS_CONFIG; + } + + //默认返回false + bool dropAble() const override { + return _flags & MK_FRAME_FLAG_DROP_ABLE; + } + + //默认返回true + bool decodeAble() const override { + return !(_flags & MK_FRAME_FLAG_NOT_DECODE_ABLE); + } + +private: + uint32_t _flags; + on_mk_frame_data_release _cb; + void *_user_data; + bool _cache_able; +}; + +static mk_frame mk_frame_create_complex(int codec_id, uint32_t dts, uint32_t pts, uint32_t frame_flags, size_t prefix_size, + char *data, size_t size, on_mk_frame_data_release cb, void *user_data) { + switch (codec_id) { + case CodecH264: + return new Frame::Ptr(new H264FrameHelper(cb, frame_flags, cb, user_data, (CodecId) codec_id, + data, size, dts, pts, prefix_size)); + case CodecH265: + return new Frame::Ptr(new H265FrameHelper(cb, frame_flags, cb, user_data, (CodecId) codec_id, + data, size, dts, pts, prefix_size)); + default: + return new Frame::Ptr(new FrameFromPtrForC(cb, frame_flags, cb, user_data, (CodecId) codec_id, data, + size, dts, pts, prefix_size)); + } +} + +API_EXPORT mk_frame API_CALL mk_frame_create(int codec_id, uint32_t dts, uint32_t pts, const char *data, size_t size, + on_mk_frame_data_release cb, void *user_data) { + + switch (codec_id) { + case CodecH264: + case CodecH265: + return mk_frame_create_complex(codec_id, dts, pts, 0, prefixSize(data, size), (char *)data, size, cb, user_data); + + case CodecAAC: { + int prefix = 0; + if ((((uint8_t *) data)[0] == 0xFF && (((uint8_t *) data)[1] & 0xF0) == 0xF0) && size > ADTS_HEADER_LEN) { + prefix = ADTS_HEADER_LEN; + } + return mk_frame_create_complex(codec_id, dts, pts, 0, prefix, (char *)data, size, cb, user_data); + } + + default: + return mk_frame_create_complex(codec_id, dts, pts, 0, 0, (char *)data, size, cb, user_data); + } +} + +API_EXPORT void API_CALL mk_frame_unref(mk_frame frame) { + assert(frame); + delete (Frame::Ptr *) frame; +} + +API_EXPORT mk_frame API_CALL mk_frame_ref(mk_frame frame) { + assert(frame); + return new Frame::Ptr(Frame::getCacheAbleFrame(*((Frame::Ptr *) frame))); +} + +API_EXPORT int API_CALL mk_frame_codec_id(mk_frame frame) { + assert(frame); + return (*((Frame::Ptr *) frame))->getCodecId(); +} + +API_EXPORT const char *API_CALL mk_frame_codec_name(mk_frame frame) { + assert(frame); + return (*((Frame::Ptr *) frame))->getCodecName(); +} + +API_EXPORT int API_CALL mk_frame_is_video(mk_frame frame) { + assert(frame); + return (*((Frame::Ptr *) frame))->getTrackType() == TrackVideo; +} + +API_EXPORT const char *API_CALL mk_frame_get_data(mk_frame frame) { + assert(frame); + return (*((Frame::Ptr *) frame))->data(); +} + +API_EXPORT size_t API_CALL mk_frame_get_data_size(mk_frame frame) { + assert(frame); + return (*((Frame::Ptr *) frame))->size(); +} + +API_EXPORT size_t API_CALL mk_frame_get_data_prefix_size(mk_frame frame) { + assert(frame); + return (*((Frame::Ptr *) frame))->prefixSize(); +} + +API_EXPORT uint32_t API_CALL mk_frame_get_dts(mk_frame frame) { + assert(frame); + return (*((Frame::Ptr *) frame))->dts(); +} + +API_EXPORT uint32_t API_CALL mk_frame_get_pts(mk_frame frame) { + assert(frame); + return (*((Frame::Ptr *) frame))->pts(); +} + +API_EXPORT uint32_t API_CALL mk_frame_get_flags(mk_frame frame) { + assert(frame); + auto &ref = *((Frame::Ptr *) frame); + uint32_t ret = 0; + if (ref->keyFrame()) { + ret &= MK_FRAME_FLAG_IS_KEY; + } + if (ref->configFrame()) { + ret &= MK_FRAME_FLAG_IS_CONFIG; + } + if (ref->dropAble()) { + ret &= MK_FRAME_FLAG_DROP_ABLE; + } + if (!ref->decodeAble()) { + ret &= MK_FRAME_FLAG_NOT_DECODE_ABLE; + } + return ret; +} \ No newline at end of file diff --git a/api/source/mk_media.cpp b/api/source/mk_media.cpp index 02465890..9ee56fe6 100755 --- a/api/source/mk_media.cpp +++ b/api/source/mk_media.cpp @@ -178,7 +178,7 @@ API_EXPORT void API_CALL mk_media_release(mk_media ctx) { delete obj; } -API_EXPORT int API_CALL mk_media_init_video(mk_media ctx, int codec_id, int width, int height, float fps){ +API_EXPORT int API_CALL mk_media_init_video(mk_media ctx, int codec_id, int width, int height, float fps, int bit_rate){ assert(ctx); MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; VideoInfo info; @@ -186,6 +186,8 @@ API_EXPORT int API_CALL mk_media_init_video(mk_media ctx, int codec_id, int widt info.iFrameRate = fps; info.iWidth = width; info.iHeight = height; + info.iBitRate = bit_rate; + (*obj)->getChannel()->initVideo(info); return (*obj)->getChannel()->initVideo(info); } @@ -200,12 +202,24 @@ API_EXPORT int API_CALL mk_media_init_audio(mk_media ctx, int codec_id, int samp return (*obj)->getChannel()->initAudio(info); } +API_EXPORT void API_CALL mk_media_init_track(mk_media ctx, mk_track track){ + assert(ctx && track); + MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; + (*obj)->getChannel()->addTrack(*((Track::Ptr *) track)); +} + API_EXPORT void API_CALL mk_media_init_complete(mk_media ctx){ assert(ctx); MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; (*obj)->getChannel()->addTrackCompleted(); } +API_EXPORT int API_CALL mk_media_input_frame(mk_media ctx, mk_frame frame){ + assert(ctx && frame); + MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; + return (*obj)->getChannel()->inputFrame(*((Frame::Ptr *) frame)); +} + API_EXPORT int API_CALL mk_media_input_h264(mk_media ctx, const void *data, int len, uint32_t dts, uint32_t pts) { assert(ctx && data && len > 0); MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; @@ -218,6 +232,12 @@ API_EXPORT int API_CALL mk_media_input_h265(mk_media ctx, const void *data, int return (*obj)->getChannel()->inputH265((const char *) data, len, dts, pts); } +API_EXPORT void API_CALL mk_media_input_yuv(mk_media ctx, const char *yuv[3], int linesize[3], uint32_t cts) { + assert(ctx && yuv && linesize); + MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; + (*obj)->getChannel()->inputYUV((char **) yuv, linesize, cts); +} + API_EXPORT int API_CALL mk_media_input_aac(mk_media ctx, const void *data, int len, uint32_t dts, void *adts) { assert(ctx && data && len > 0 && adts); MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; diff --git a/api/source/mk_player.cpp b/api/source/mk_player.cpp index c0e0087f..b73ed110 100755 --- a/api/source/mk_player.cpp +++ b/api/source/mk_player.cpp @@ -47,51 +47,34 @@ public: }); } - void unset(){ + void unset() { + for (auto &track : _player->getTracks(false)) { + track->clear(); + } lock_guard lck(_mtx); _on_play = nullptr; _on_shutdown = nullptr; - _on_data = nullptr; } void onEvent(bool is_shutdown, const SockException &ex){ lock_guard lck(_mtx); - if(is_shutdown){ + if (is_shutdown) { //播放中断 - if(_on_shutdown){ - _on_shutdown(_on_shutdown_data,ex.getErrCode(),ex.what()); + if (_on_shutdown) { + _on_shutdown(_on_shutdown_data, ex.getErrCode(), ex.what(), nullptr, 0); } return; } //播放结果 - if(_on_play){ - _on_play(_on_play_data,ex.getErrCode(),ex.what()); - } - - if(ex){ - //播放失败 - return; - } - - //播放成功,添加事件回调 - weak_ptr weak_self = shared_from_this(); - auto delegate = std::make_shared([weak_self](const Frame::Ptr &frame) { - if (auto strong_self = weak_self.lock()) { - strong_self->onData(frame); - return true; + if (_on_play) { + auto cpp_tracks = _player->getTracks(false); + mk_track tracks[TrackMax] = {nullptr}; + int track_count = 0; + for (auto &track : cpp_tracks) { + tracks[track_count++] = (mk_track) &track; } - return false; - }); - for (auto &track : _player->getTracks(false)) { - track->addDelegate(delegate); - } - } - - void onData(const Frame::Ptr &frame){ - lock_guard lck(_mtx); - if(_on_data){ - _on_data(_on_data_data,frame->getTrackType(),frame->getCodecId(),frame->data(),frame->size(),frame->dts(),frame->pts()); + _on_play(_on_play_data, ex.getErrCode(), ex.what(), tracks, track_count); } } @@ -106,12 +89,6 @@ public: } } - void setOnData(on_mk_play_data cb, void *user_data) { - lock_guard lck(_mtx); - _on_data_data = user_data; - _on_data = cb; - } - MediaPlayer::Ptr& getPlayer(){ return _player; } @@ -119,12 +96,10 @@ private: MediaPlayer::Ptr _player; recursive_mutex _mtx; on_mk_play_event _on_play = nullptr; - on_mk_play_data _on_data = nullptr; on_mk_play_event _on_shutdown = nullptr; void *_on_play_data = nullptr; void *_on_shutdown_data = nullptr; - void *_on_data_data = nullptr; }; API_EXPORT mk_player API_CALL mk_player_create() { @@ -214,90 +189,6 @@ API_EXPORT void API_CALL mk_player_set_on_shutdown(mk_player ctx, on_mk_play_eve mk_player_set_on_event(ctx,cb,user_data,1); } -API_EXPORT void API_CALL mk_player_set_on_data(mk_player ctx, on_mk_play_data cb, void *user_data) { - assert(ctx); - MediaPlayerForC &obj = **((MediaPlayerForC::Ptr *)ctx); - obj.setOnData(cb,user_data); -} - -API_EXPORT int API_CALL mk_player_video_codec_id(mk_player ctx){ - assert(ctx); - MediaPlayerForC &obj = **((MediaPlayerForC::Ptr *)ctx); - auto track = dynamic_pointer_cast(obj->getTrack(TrackVideo)); - return track ? track->getCodecId() : CodecInvalid; -} - -API_EXPORT int API_CALL mk_player_video_codec_id_vendor_head(mk_player ctx, char *vendor, char *head, int *head_len) { - assert(ctx); - MediaPlayerForC &obj = **((MediaPlayerForC::Ptr *) ctx); - auto track = dynamic_pointer_cast(obj->getTrack(TrackVideo)); - int codecId = track ? track->getCodecId() : CodecInvalid; - if (codecId == CodecH264) { - auto h264Track = dynamic_pointer_cast(obj->getTrack(TrackVideo)); - auto pps = h264Track->getPps(); - auto ppsLen = pps.size(); - if (ppsLen >= (4 + 16)) { - std::string temVendor = std::string(pps.c_str() + 4, 16); - memcpy(vendor, temVendor.c_str(), temVendor.length()); - if (ppsLen > (4 + 16)) { - std::string temHead = std::string(pps.c_str() + 20, ppsLen - 20); - memcpy(head, temHead.c_str(), temHead.length()); - *head_len = temHead.length(); - } - } - } - return codecId; -} - -API_EXPORT int API_CALL mk_player_video_width(mk_player ctx) { - assert(ctx); - MediaPlayerForC &obj = **((MediaPlayerForC::Ptr *)ctx); - auto track = dynamic_pointer_cast(obj->getTrack(TrackVideo)); - return track ? track->getVideoWidth() : 0; -} - -API_EXPORT int API_CALL mk_player_video_height(mk_player ctx) { - assert(ctx); - MediaPlayerForC &obj = **((MediaPlayerForC::Ptr *)ctx); - auto track = dynamic_pointer_cast(obj->getTrack(TrackVideo)); - return track ? track->getVideoHeight() : 0; -} - -API_EXPORT float API_CALL mk_player_video_fps(mk_player ctx) { - assert(ctx); - MediaPlayerForC &obj = **((MediaPlayerForC::Ptr *)ctx); - auto track = dynamic_pointer_cast(obj->getTrack(TrackVideo)); - return track ? track->getVideoFps() : 0; -} - -API_EXPORT int API_CALL mk_player_audio_codec_id(mk_player ctx){ - assert(ctx); - MediaPlayerForC &obj = **((MediaPlayerForC::Ptr *)ctx); - auto track = dynamic_pointer_cast(obj->getTrack(TrackAudio)); - return track ? track->getCodecId() : CodecInvalid; -} - -API_EXPORT int API_CALL mk_player_audio_samplerate(mk_player ctx) { - assert(ctx); - MediaPlayerForC &obj = **((MediaPlayerForC::Ptr *)ctx); - auto track = dynamic_pointer_cast(obj->getTrack(TrackAudio)); - return track ? track->getAudioSampleRate() : 0; -} - -API_EXPORT int API_CALL mk_player_audio_bit(mk_player ctx) { - assert(ctx); - MediaPlayerForC &obj = **((MediaPlayerForC::Ptr *)ctx); - auto track = dynamic_pointer_cast(obj->getTrack(TrackAudio)); - return track ? track->getAudioSampleBit() : 0; -} - -API_EXPORT int API_CALL mk_player_audio_channel(mk_player ctx) { - assert(ctx); - MediaPlayerForC &obj = **((MediaPlayerForC::Ptr *)ctx); - auto track = dynamic_pointer_cast(obj->getTrack(TrackAudio)); - return track ? track->getAudioChannel() : 0; -} - API_EXPORT float API_CALL mk_player_duration(mk_player ctx) { assert(ctx); MediaPlayerForC &obj = **((MediaPlayerForC::Ptr *)ctx); diff --git a/api/source/mk_tcp.cpp b/api/source/mk_tcp.cpp index e0493a0d..ea15fdb5 100644 --- a/api/source/mk_tcp.cpp +++ b/api/source/mk_tcp.cpp @@ -13,10 +13,76 @@ #include "mk_tcp_private.h" #include "Http/WebSocketClient.h" #include "Http/WebSocketSession.h" +#include "Network/Buffer.h" using namespace toolkit; using namespace mediakit; +class BufferForC : public Buffer { +public: + BufferForC(const char *data, size_t len, on_mk_buffer_free cb, void *user_data) { + if (len <= 0) { + len = strlen(data); + } + if (!cb) { + auto ptr = malloc(len); + memcpy(ptr, data, len); + data = (char *) ptr; + + cb = [](void *user_data, void *data) { + free(data); + }; + } + _data = (char *) data; + _size = len; + _cb = cb; + _user_data = user_data; + } + + ~BufferForC() override { + _cb(_user_data, _data); + } + + char *data() const override { + return _data; + } + + size_t size() const override { + return _size; + } + +private: + char *_data; + size_t _size; + on_mk_buffer_free _cb; + void *_user_data; +}; + +API_EXPORT mk_buffer API_CALL mk_buffer_from_char(const char *data, size_t len, on_mk_buffer_free cb, void *user_data) { + assert(data); + return new Buffer::Ptr(std::make_shared(data, len, cb, user_data)); +} + +API_EXPORT mk_buffer API_CALL mk_buffer_ref(mk_buffer buffer) { + assert(buffer); + return new Buffer::Ptr(*((Buffer::Ptr *) buffer)); +} + +API_EXPORT void API_CALL mk_buffer_unref(mk_buffer buffer) { + assert(buffer); + delete (Buffer::Ptr *) buffer; +} + +API_EXPORT const char *API_CALL mk_buffer_get_data(mk_buffer buffer) { + assert(buffer); + return (*((Buffer::Ptr *) buffer))->data(); +} + +API_EXPORT size_t API_CALL mk_buffer_get_size(mk_buffer buffer) { + assert(buffer); + return (*((Buffer::Ptr *) buffer))->size(); +} + API_EXPORT const char* API_CALL mk_sock_info_peer_ip(const mk_sock_info ctx, char *buf){ assert(ctx); SockInfo *sock = (SockInfo *)ctx; @@ -53,34 +119,54 @@ API_EXPORT void API_CALL mk_tcp_session_shutdown(const mk_tcp_session ctx,int er session->safeShutdown(SockException((ErrCode)err,err_msg)); } -API_EXPORT void API_CALL mk_tcp_session_send(const mk_tcp_session ctx,const char *data, size_t len){ - assert(ctx && data); - if(!len){ - len = strlen(data); - } - TcpSessionForC *session = (TcpSessionForC *)ctx; - session->SockSender::send(data,len); +API_EXPORT void API_CALL mk_tcp_session_send_buffer(const mk_tcp_session ctx, mk_buffer buffer) { + assert(ctx && buffer); + TcpSessionForC *session = (TcpSessionForC *) ctx; + session->send(*((Buffer::Ptr *) buffer)); } -API_EXPORT void API_CALL mk_tcp_session_send_safe(const mk_tcp_session ctx,const char *data,size_t len){ - assert(ctx && data); - if (!len) { - len = strlen(data); - } +API_EXPORT void API_CALL mk_tcp_session_send(const mk_tcp_session ctx, const char *data, size_t len) { + auto buffer = mk_buffer_from_char(data, len, nullptr, nullptr); + mk_tcp_session_send_buffer(ctx, buffer); + mk_buffer_unref(buffer); +} + +API_EXPORT void API_CALL mk_tcp_session_send_buffer_safe(const mk_tcp_session ctx, mk_buffer buffer) { + assert(ctx && buffer); try { - std::weak_ptr weak_session = ((TcpSessionForC *)ctx)->shared_from_this(); - std::string str = std::string(data,len); - ((TcpSessionForC *)ctx)->async([weak_session,str](){ + std::weak_ptr weak_session = ((TcpSessionForC *) ctx)->shared_from_this(); + auto ref = mk_buffer_ref(buffer); + ((TcpSessionForC *) ctx)->async([weak_session, ref]() { auto session_session = weak_session.lock(); - if(session_session){ - session_session->SockSender::send(str); + if (session_session) { + session_session->send(*((Buffer::Ptr *) ref)); } + mk_buffer_unref(ref); }); } catch (std::exception &ex) { WarnL << "can not got the strong pionter of this mk_tcp_session:" << ex.what(); } } +API_EXPORT mk_tcp_session_ref API_CALL mk_tcp_session_ref_from(const mk_tcp_session ctx) { + auto ref = ((TcpSessionForC *) ctx)->shared_from_this(); + return new std::shared_ptr(std::dynamic_pointer_cast(ref)); +} + +API_EXPORT void mk_tcp_session_ref_release(const mk_tcp_session_ref ref) { + delete (std::shared_ptr *) ref; +} + +API_EXPORT mk_tcp_session mk_tcp_session_from_ref(const mk_tcp_session_ref ref) { + return ((std::shared_ptr *) ref)->get(); +} + +API_EXPORT void API_CALL mk_tcp_session_send_safe(const mk_tcp_session ctx, const char *data, size_t len) { + auto buffer = mk_buffer_from_char(data, len, nullptr, nullptr); + mk_tcp_session_send_buffer_safe(ctx, buffer); + mk_buffer_unref(buffer); +} + ////////////////////////////////////////TcpSessionForC//////////////////////////////////////////////// static TcpServer::Ptr s_tcp_server[4]; static mk_tcp_session_events s_events_server = {0}; @@ -94,7 +180,7 @@ TcpSessionForC::TcpSessionForC(const Socket::Ptr &pSock) : TcpSession(pSock) { void TcpSessionForC::onRecv(const Buffer::Ptr &buffer) { if (s_events_server.on_mk_tcp_session_data) { - s_events_server.on_mk_tcp_session_data(_local_port,this, buffer->data(), buffer->size()); + s_events_server.on_mk_tcp_session_data(_local_port, this, (mk_buffer)&buffer); } } @@ -169,10 +255,9 @@ TcpClientForC::TcpClientForC(mk_tcp_client_events *events){ _events = *events; } - void TcpClientForC::onRecv(const Buffer::Ptr &pBuf) { - if(_events.on_mk_tcp_client_data){ - _events.on_mk_tcp_client_data(_client,pBuf->data(),pBuf->size()); + if (_events.on_mk_tcp_client_data) { + _events.on_mk_tcp_client_data(_client, (mk_buffer)&pBuf); } } @@ -246,24 +331,36 @@ API_EXPORT void API_CALL mk_tcp_client_connect(mk_tcp_client ctx, const char *ho (*client)->startConnect(host,port); } -API_EXPORT void API_CALL mk_tcp_client_send(mk_tcp_client ctx, const char *data, int len){ - assert(ctx && data); - TcpClientForC::Ptr *client = (TcpClientForC::Ptr *)ctx; - (*client)->SockSender::send(data,len); +API_EXPORT void API_CALL mk_tcp_client_send_buffer(mk_tcp_client ctx, mk_buffer buffer) { + assert(ctx && buffer); + TcpClientForC::Ptr *client = (TcpClientForC::Ptr *) ctx; + (*client)->send(*((Buffer::Ptr *) buffer)); +} + +API_EXPORT void API_CALL mk_tcp_client_send(mk_tcp_client ctx, const char *data, int len) { + auto buffer = mk_buffer_from_char(data, len, nullptr, nullptr); + mk_tcp_client_send_buffer(ctx, buffer); + mk_buffer_unref(buffer); +} + +API_EXPORT void API_CALL mk_tcp_client_send_buffer_safe(mk_tcp_client ctx, mk_buffer buffer) { + assert(ctx && buffer); + TcpClientForC::Ptr *client = (TcpClientForC::Ptr *) ctx; + std::weak_ptr weakClient = *client; + auto ref = mk_buffer_ref(buffer); + (*client)->async([weakClient, ref]() { + auto strongClient = weakClient.lock(); + if (strongClient) { + strongClient->send(*((Buffer::Ptr *) ref)); + } + mk_buffer_unref(ref); + }); } API_EXPORT void API_CALL mk_tcp_client_send_safe(mk_tcp_client ctx, const char *data, int len){ - assert(ctx && data); - TcpClientForC::Ptr *client = (TcpClientForC::Ptr *)ctx; - std::weak_ptr weakClient = *client; - auto buf = BufferRaw::create(); - buf->assign(data,len); - (*client)->async([weakClient,buf](){ - auto strongClient = weakClient.lock(); - if(strongClient){ - strongClient->send(buf); - } - }); + auto buffer = mk_buffer_from_char(data, len, nullptr, nullptr); + mk_tcp_client_send_buffer_safe(ctx, buffer); + mk_buffer_unref(buffer); } API_EXPORT void API_CALL mk_tcp_client_set_user_data(mk_tcp_client ctx,void *user_data){ diff --git a/api/source/mk_thread.cpp b/api/source/mk_thread.cpp index 63efb1a3..42ed5fef 100644 --- a/api/source/mk_thread.cpp +++ b/api/source/mk_thread.cpp @@ -44,6 +44,15 @@ API_EXPORT void API_CALL mk_async_do(mk_thread ctx,on_mk_async cb, void *user_da }); } +API_EXPORT void API_CALL mk_async_do_delay(mk_thread ctx, size_t ms, on_mk_async cb, void *user_data) { + assert(ctx && cb && ms); + EventPoller *poller = (EventPoller *) ctx; + poller->doDelayTask(ms, [cb, user_data]() { + cb(user_data); + return 0; + }); +} + API_EXPORT void API_CALL mk_sync_do(mk_thread ctx,on_mk_async cb, void *user_data){ assert(ctx && cb); EventPoller *poller = (EventPoller *)ctx; @@ -107,4 +116,52 @@ API_EXPORT void API_CALL mk_timer_release(mk_timer ctx){ TimerForC::Ptr *obj = (TimerForC::Ptr *)ctx; (*obj)->cancel(); delete obj; +} + +class WorkThreadPoolForC : public TaskExecutorGetterImp { +public: + ~WorkThreadPoolForC() override = default; + + WorkThreadPoolForC(const char *name, size_t n_thread, int priority) { + //最低优先级 + addPoller(name, n_thread, (ThreadPool::Priority) priority, false); + } + + EventPoller::Ptr getPoller() { + return dynamic_pointer_cast(getExecutor()); + } +}; + +API_EXPORT mk_thread_pool API_CALL mk_thread_pool_create(const char *name, size_t n_thread, int priority) { + return new WorkThreadPoolForC(name, n_thread, priority); +} + +API_EXPORT int API_CALL mk_thread_pool_release(mk_thread_pool pool) { + assert(pool); + delete (WorkThreadPoolForC *) pool; + return 0; +} + +API_EXPORT mk_thread API_CALL mk_thread_from_thread_pool(mk_thread_pool pool) { + assert(pool); + return ((WorkThreadPoolForC *) pool)->getPoller().get(); +} + +API_EXPORT mk_sem API_CALL mk_sem_create() { + return new toolkit::semaphore; +} + +API_EXPORT void API_CALL mk_sem_release(mk_sem sem) { + assert(sem); + delete (toolkit::semaphore *) sem; +} + +API_EXPORT void API_CALL mk_sem_post(mk_sem sem, size_t n) { + assert(sem); + ((toolkit::semaphore *) sem)->post(n); +} + +API_EXPORT void API_CALL mk_sem_wait(mk_sem sem) { + assert(sem); + ((toolkit::semaphore *) sem)->wait(); } \ No newline at end of file diff --git a/api/source/mk_track.cpp b/api/source/mk_track.cpp new file mode 100644 index 00000000..a9891d95 --- /dev/null +++ b/api/source/mk_track.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "mk_track.h" +#include "Extension/Track.h" + +using namespace std; +using namespace toolkit; +using namespace mediakit; + +class VideoTrackForC : public VideoTrack { +public: + VideoTrackForC(int codec_id, codec_args *args) { + _codec_id = (CodecId) codec_id; + if (args) { + _args = *args; + } else { + memset(&_args, 0, sizeof(_args)); + } + } + + ~VideoTrackForC() override = default; + + int getVideoHeight() const override { + return _args.video.height; + } + + int getVideoWidth() const override { + return _args.video.width; + } + + float getVideoFps() const override { + return _args.video.fps; + } + + CodecId getCodecId() const override { + return _codec_id; + } + + bool ready() override { + return true; + } + + Track::Ptr clone() override { + return std::make_shared::type>(*this); + } + + Sdp::Ptr getSdp() override { + return nullptr; + } + +private: + CodecId _codec_id; + codec_args _args; +}; + +class AudioTrackForC : public AudioTrackImp { +public: + ~AudioTrackForC() override = default; + + AudioTrackForC(int codec_id, codec_args *args) : + AudioTrackImp((CodecId) codec_id, args->audio.sample_rate, args->audio.channels, 16) {} + + Track::Ptr clone() override { + return std::make_shared::type>(*this); + } + + Sdp::Ptr getSdp() override { + return nullptr; + } +}; + +API_EXPORT mk_track API_CALL mk_track_create(int codec_id, codec_args *args) { + switch (getTrackType((CodecId) codec_id)) { + case TrackVideo: return new Track::Ptr(std::make_shared(codec_id, args)); + case TrackAudio: return new Track::Ptr(std::make_shared(codec_id, args)); + default: WarnL << "unrecognized codec:" << codec_id; return nullptr; + } +} + +API_EXPORT void API_CALL mk_track_unref(mk_track track) { + assert(track); + delete (Track::Ptr *)track; +} + +API_EXPORT mk_track API_CALL mk_track_ref(mk_track track) { + assert(track); + return new Track::Ptr(*( (Track::Ptr *)track)); +} + +API_EXPORT int API_CALL mk_track_codec_id(mk_track track) { + assert(track); + return (*((Track::Ptr *) track))->getCodecId(); +} + +API_EXPORT const char *API_CALL mk_track_codec_name(mk_track track) { + assert(track); + return (*((Track::Ptr *) track))->getCodecName(); +} + +API_EXPORT int API_CALL mk_track_bit_rate(mk_track track) { + assert(track); + return (*((Track::Ptr *) track))->getBitRate(); +} + +API_EXPORT void *API_CALL mk_track_add_delegate(mk_track track, on_mk_frame_out cb, void *user_data) { + assert(track && cb); + auto delegate = std::make_shared([cb, user_data](const Frame::Ptr &frame) { + cb(user_data, (mk_frame) &frame); + return true; + }); + (*((Track::Ptr *) track))->addDelegate(delegate); + return delegate.get(); +} + +API_EXPORT void API_CALL mk_track_del_delegate(mk_track track, void *tag) { + assert(track && tag); + (*((Track::Ptr *) track))->delDelegate((FrameWriterInterface *) tag); +} + +API_EXPORT void API_CALL mk_track_input_frame(mk_track track, mk_frame frame) { + assert(track && frame); + (*((Track::Ptr *) track))->inputFrame((*((Frame::Ptr *) frame))); +} + +API_EXPORT int API_CALL mk_track_is_video(mk_track track) { + assert(track); + return (*((Track::Ptr *) track))->getTrackType() == TrackVideo; +} + +API_EXPORT int API_CALL mk_track_video_width(mk_track track) { + assert(track); + auto video = dynamic_pointer_cast((*((Track::Ptr *) track))); + if (video) { + return video->getVideoWidth(); + } + WarnL << "not video track"; + return 0; +} + +API_EXPORT int API_CALL mk_track_video_height(mk_track track) { + assert(track); + auto video = dynamic_pointer_cast((*((Track::Ptr *) track))); + if (video) { + return video->getVideoHeight(); + } + WarnL << "not video track"; + return 0; +} + +API_EXPORT int API_CALL mk_track_video_fps(mk_track track) { + assert(track); + auto video = dynamic_pointer_cast((*((Track::Ptr *) track))); + if (video) { + return video->getVideoFps(); + } + WarnL << "not video track"; + return 0; +} + +API_EXPORT int API_CALL mk_track_audio_sample_rate(mk_track track) { + assert(track); + auto audio = dynamic_pointer_cast((*((Track::Ptr *) track))); + if (audio) { + return audio->getAudioSampleRate(); + } + WarnL << "not audio track"; + return 0; +} + +API_EXPORT int API_CALL mk_track_audio_channel(mk_track track) { + assert(track); + auto audio = dynamic_pointer_cast((*((Track::Ptr *) track))); + if (audio) { + return audio->getAudioChannel(); + } + WarnL << "not audio track"; + return 0; +} + +API_EXPORT int API_CALL mk_track_audio_sample_bit(mk_track track) { + assert(track); + auto audio = dynamic_pointer_cast((*((Track::Ptr *) track))); + if (audio) { + return audio->getAudioSampleBit(); + } + WarnL << "not audio track"; + return 0; +} \ No newline at end of file diff --git a/api/source/mk_transcode.cpp b/api/source/mk_transcode.cpp new file mode 100644 index 00000000..c0030549 --- /dev/null +++ b/api/source/mk_transcode.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "mk_transcode.h" +#include "Extension/Track.h" + +using namespace mediakit; + +#ifdef ENABLE_FFMPEG + +#include "Codec/Transcode.h" + +API_EXPORT mk_decoder API_CALL mk_decoder_create(mk_track track, int thread_num) { + assert(track); + return new FFmpegDecoder(*((Track::Ptr *)track), thread_num); +} + +API_EXPORT void API_CALL mk_decoder_release(mk_decoder ctx, int flush_frame) { + assert(ctx); + auto decoder = (FFmpegDecoder *)ctx; + if (flush_frame) { + decoder->stopThread(false); + } + delete decoder; +} + +API_EXPORT void API_CALL mk_decoder_decode(mk_decoder ctx, mk_frame frame, int async, int enable_merge) { + assert(ctx && frame); + ((FFmpegDecoder *)ctx)->inputFrame(*((Frame::Ptr *)frame), false, async, enable_merge); +} + +API_EXPORT void API_CALL mk_decoder_set_max_async_frame_size(mk_decoder ctx, size_t size) { + assert(ctx && size); + ((FFmpegDecoder *)ctx)->setMaxTaskSize(size); +} + +API_EXPORT void API_CALL mk_decoder_set_cb(mk_decoder ctx, on_mk_decode cb, void *user_data) { + assert(ctx && cb); + ((FFmpegDecoder *)ctx)->setOnDecode([cb, user_data](const FFmpegFrame::Ptr &pix_frame) { + cb(user_data, (mk_frame_pix)&pix_frame); + }); +} + +API_EXPORT const AVCodecContext *API_CALL mk_decoder_get_context(mk_decoder ctx) { + assert(ctx); + return ((FFmpegDecoder *)ctx)->getContext(); +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +API_EXPORT mk_frame_pix API_CALL mk_frame_pix_ref(mk_frame_pix frame) { + assert(frame); + return new FFmpegFrame::Ptr(*(FFmpegFrame::Ptr *)frame); +} + +API_EXPORT mk_frame_pix API_CALL mk_frame_pix_from_av_frame(AVFrame *frame) { + assert(frame); + return new FFmpegFrame::Ptr(std::make_shared( + std::shared_ptr(av_frame_clone(frame), [](AVFrame *frame) { av_frame_free(&frame); }))); +} + +API_EXPORT void API_CALL mk_frame_pix_unref(mk_frame_pix frame) { + assert(frame); + delete (FFmpegFrame::Ptr *)frame; +} + +API_EXPORT AVFrame *API_CALL mk_frame_pix_get_av_frame(mk_frame_pix frame) { + assert(frame); + return (*(FFmpegFrame::Ptr *)frame)->get(); +} + +////////////////////////////////////////////////////////////////////////////////// + +API_EXPORT mk_swscale mk_swscale_create(int output, int width, int height) { + return new FFmpegSws((AVPixelFormat)output, width, height); +} + +API_EXPORT void mk_swscale_release(mk_swscale ctx) { + delete (FFmpegSws *)ctx; +} + +API_EXPORT int mk_swscale_input_frame(mk_swscale ctx, mk_frame_pix frame, uint8_t *data) { + return ((FFmpegSws *)ctx)->inputFrame(*(FFmpegFrame::Ptr *)frame, data); +} + +API_EXPORT mk_frame_pix mk_swscale_input_frame2(mk_swscale ctx, mk_frame_pix frame) { + return new FFmpegFrame::Ptr(((FFmpegSws *)ctx)->inputFrame(*(FFmpegFrame::Ptr *)frame)); +} + +API_EXPORT uint8_t **API_CALL mk_get_av_frame_data(AVFrame *frame) { + return frame->data; +} + +API_EXPORT int *API_CALL mk_get_av_frame_line_size(AVFrame *frame) { + return frame->linesize; +} + +API_EXPORT int64_t API_CALL mk_get_av_frame_dts(AVFrame *frame) { + return frame->pkt_dts; +} + +API_EXPORT int64_t API_CALL mk_get_av_frame_pts(AVFrame *frame) { + return frame->pts; +} + +API_EXPORT int API_CALL mk_get_av_frame_width(AVFrame *frame) { + return frame->width; +} + +API_EXPORT int API_CALL mk_get_av_frame_height(AVFrame *frame) { + return frame->height; +} + +API_EXPORT int API_CALL mk_get_av_frame_format(AVFrame *frame) { + return frame->format; +} + + +#endif //ENABLE_FFMPEG \ No newline at end of file diff --git a/api/tests/CMakeLists.sample b/api/tests/CMakeLists.sample new file mode 100644 index 00000000..9f31a31e --- /dev/null +++ b/api/tests/CMakeLists.sample @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.1.3) +project(player) +include_directories("../../include") +link_directories("../../so") +add_executable(player "./player_opencv.c") +target_link_libraries(player mk_api ssl crypto avcodec swscale swresample avutil x264 openh264 x265) \ No newline at end of file diff --git a/api/tests/h264_media_server.c b/api/tests/h264_media_server.c index 3ea51733..051d477c 100644 --- a/api/tests/h264_media_server.c +++ b/api/tests/h264_media_server.c @@ -22,14 +22,17 @@ static void s_on_exit(int sig) { exit_flag = 1; } -static void on_h264_frame(void *user_data, mk_h264_splitter splitter, const char *frame, int size) { +static void on_h264_frame(void *user_data, mk_h264_splitter splitter, const char *data, int size) { #ifdef _WIN32 Sleep(40); #else usleep(40 * 1000); #endif - mk_media media = (mk_media) user_data; - mk_media_input_h264(media, frame, size, 0, 0); + static int dts = 0; + mk_frame frame = mk_frame_create(MKCodecH264, dts, dts, data, size, NULL, NULL); + dts += 40; + mk_media_input_frame((mk_media) user_data, frame); + mk_frame_unref(frame); } int main(int argc, char *argv[]) { @@ -60,7 +63,7 @@ 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); + mk_media_init_video(media, 0, 0, 0, 0, 2 * 104 * 1024); mk_media_init_complete(media); //创建h264分帧器 diff --git a/api/tests/httpclient.c b/api/tests/httpclient.c new file mode 100644 index 00000000..555c9e60 --- /dev/null +++ b/api/tests/httpclient.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include "mk_mediakit.h" + +typedef struct { + mk_sem sem; + mk_http_requester requester; +} Context; + +static API_CALL void on_requester_complete(void *user_data, int code, const char *err_msg){ + Context *ctx = (Context *)user_data; + log_debug("code: %d %s", code, err_msg); + size_t res_len = 0; + log_debug("response: %s %s", mk_http_requester_get_response_status(ctx->requester), + mk_http_requester_get_response_body(ctx->requester, &res_len)); + mk_sem_post(ctx->sem, 1); +} + +int main(int argc, char *argv[]) { + mk_config config = { + .ini = NULL, + .ini_is_path = 0, + .log_level = 0, + .log_mask = LOG_CONSOLE, + .ssl = NULL, + .ssl_is_path = 1, + .ssl_pwd = NULL, + .thread_num = 0 + }; + mk_env_init(&config); + + mk_http_requester requester = mk_http_requester_create(); + mk_http_requester_set_method(requester, "POST"); + + mk_http_body body = mk_http_body_from_string("tn=monline_7_dg&ie=utf-8&wd=test", 0); + mk_http_requester_set_body(requester, body); + mk_http_body_release(body); + + mk_sem sem = mk_sem_create(); + + Context ctx = {.requester = requester, .sem = sem}; + + mk_http_requester_set_cb(requester, on_requester_complete, &ctx); + mk_http_requester_start(requester, "http://www.baidu.com/baidu", 10); + + //等待http请求完毕 + mk_sem_wait(sem); + + mk_sem_release(sem); + mk_http_requester_release(requester); + return 0; +} \ No newline at end of file diff --git a/api/tests/player_opencv.c b/api/tests/player_opencv.c new file mode 100644 index 00000000..99a5dade --- /dev/null +++ b/api/tests/player_opencv.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include +#include +#include "mk_mediakit.h" + +typedef struct { + mk_player player; + mk_decoder video_decoder; + mk_swscale swscale; +} Context; + +void API_CALL on_track_frame_out(void *user_data, mk_frame frame) { + Context *ctx = (Context *) user_data; + mk_decoder_decode(ctx->video_decoder, frame, 1, 1); +} + +void API_CALL on_frame_decode(void *user_data, mk_frame_pix frame) { + Context *ctx = (Context *) user_data; + int w = mk_get_av_frame_width(mk_frame_pix_get_av_frame(frame)); + int h = mk_get_av_frame_height(mk_frame_pix_get_av_frame(frame)); + +#if 1 + uint8_t *brg24 = malloc(w * h * 3); + mk_swscale_input_frame(ctx->swscale, frame, brg24); + free(brg24); +#else + //todo 此处转换为opencv对象 + cv::Mat *mat = new cv::Mat(); + mat->create(h, w, CV_8UC3); + mk_swscale_input_frame(ctx->swscale, frame, (uint8_t *) mat->data); +#endif + log_trace("decode frame output, pts:%d, w:%d, h:%d", mk_get_av_frame_dts(mk_frame_pix_get_av_frame(frame)), w, h); +} + +void API_CALL on_mk_play_event_func(void *user_data, int err_code, const char *err_msg, mk_track tracks[], + int track_count) { + Context *ctx = (Context *) user_data; + if (err_code == 0) { + //success + log_debug("play success!"); + for (int i = 0; i < track_count; ++i) { + if (mk_track_is_video(tracks[i])) { + log_info("got video track: %s", mk_track_codec_name(tracks[i])); + ctx->video_decoder = mk_decoder_create(tracks[i], 0); + mk_decoder_set_cb(ctx->video_decoder, on_frame_decode, user_data); + //监听track数据回调 + mk_track_add_delegate(tracks[i], on_track_frame_out, user_data); + } + } + } else { + log_warn("play failed: %d %s", err_code, err_msg); + } +} + +void API_CALL on_mk_shutdown_func(void *user_data, int err_code, const char *err_msg, mk_track tracks[], int track_count) { + log_warn("play interrupted: %d %s", err_code, err_msg); +} + +int main(int argc, char *argv[]) { + mk_config config = { + .ini = NULL, + .ini_is_path = 0, + .log_level = 0, + .log_mask = LOG_CONSOLE, + .ssl = NULL, + .ssl_is_path = 1, + .ssl_pwd = NULL, + .thread_num = 0 + }; + mk_env_init(&config); + if (argc != 2) { + printf("Usage: ./player url\n"); + return -1; + } + + Context ctx; + memset(&ctx, 0, sizeof(Context)); + + ctx.player = mk_player_create(); + ctx.swscale = mk_swscale_create(3, 0, 0); + mk_player_set_on_result(ctx.player, on_mk_play_event_func, &ctx); + mk_player_set_on_shutdown(ctx.player, on_mk_shutdown_func, &ctx); + mk_player_play(ctx.player, argv[1]); + + log_info("enter any key to exit"); + getchar(); + + if (ctx.player) { + mk_player_release(ctx.player); + } + if (ctx.video_decoder) { + mk_decoder_release(ctx.video_decoder, 1); + } + if (ctx.swscale) { + mk_swscale_release(ctx.swscale); + } + return 0; +} \ No newline at end of file diff --git a/api/tests/pusher.c b/api/tests/pusher.c index a4180588..cbcd3c8f 100644 --- a/api/tests/pusher.c +++ b/api/tests/pusher.c @@ -8,16 +8,9 @@ * may be found in the AUTHORS file in the root of the source tree. */ -#include #include #include "mk_mediakit.h" -#ifdef _WIN32 -#include "windows.h" -#else -#include "unistd.h" -#endif - typedef struct { mk_player player; mk_media media; @@ -84,7 +77,12 @@ void API_CALL on_mk_media_source_regist_func(void *user_data, mk_media_source se } } -void API_CALL on_mk_play_event_func(void *user_data, int err_code, const char *err_msg) { +void API_CALL on_track_frame_out(void *user_data, mk_frame frame) { + Context *ctx = (Context *) user_data; + mk_media_input_frame(ctx->media, frame); +} + +void API_CALL on_mk_play_event_func(void *user_data, int err_code, const char *err_msg, mk_track tracks[], int track_count) { Context *ctx = (Context *) user_data; release_media(&(ctx->media)); release_pusher(&(ctx->pusher)); @@ -92,21 +90,9 @@ void API_CALL on_mk_play_event_func(void *user_data, int err_code, const char *e //success log_debug("play success!"); ctx->media = mk_media_create("__defaultVhost__", "live", "test", 0, 0, 0); - - int video_codec = mk_player_video_codec_id(ctx->player); - int audio_codec = mk_player_audio_codec_id(ctx->player); - if(video_codec != -1){ - mk_media_init_video(ctx->media, video_codec, - mk_player_video_width(ctx->player), - mk_player_video_height(ctx->player), - mk_player_video_fps(ctx->player)); - } - - if(audio_codec != -1){ - mk_media_init_audio(ctx->media,audio_codec, - mk_player_audio_samplerate(ctx->player), - mk_player_audio_channel(ctx->player), - mk_player_audio_bit(ctx->player)); + for (int i = 0; i < track_count; ++i) { + mk_media_init_track(ctx->media, tracks[i]); + mk_track_add_delegate(tracks[i], on_track_frame_out, user_data); } mk_media_init_complete(ctx->media); mk_media_set_on_regist(ctx->media, on_mk_media_source_regist_func, ctx); @@ -116,49 +102,15 @@ void API_CALL on_mk_play_event_func(void *user_data, int err_code, const char *e } } -void API_CALL on_mk_play_data_func(void *user_data,int track_type, int codec_id,void *data,size_t len, uint32_t dts,uint32_t pts){ - Context *ctx = (Context *) user_data; - switch (codec_id) { - case 0 : { - //h264 - mk_media_input_h264(ctx->media, data, (int)len, dts, pts); - break; - } - case 1 : { - //h265 - mk_media_input_h265(ctx->media, data, (int)len, dts, pts); - break; - } - case 2 : { - //aac, aac头7个字节 - mk_media_input_aac(ctx->media, (uint8_t*)data + 7, (int)len, dts, data); - break; - } - case 3 : //g711a - case 4 : //g711u - case 5 : //opus - mk_media_input_audio(ctx->media, data, (int) len, dts); - break; - - default: { - log_warn("unknown codec: %d", codec_id); - break; - } - } -} - void context_start(Context *ctx, const char *url_pull, const char *url_push){ release_player(&(ctx->player)); ctx->player = mk_player_create(); mk_player_set_on_result(ctx->player, on_mk_play_event_func, ctx); mk_player_set_on_shutdown(ctx->player, on_mk_play_event_func, ctx); - mk_player_set_on_data(ctx->player, on_mk_play_data_func, ctx); mk_player_play(ctx->player, url_pull); strcpy(ctx->push_url, url_push); } -//create_player("http://hls.weathertv.cn/tslslive/qCFIfHB/hls/live_sd.m3u8"); - int main(int argc, char *argv[]){ mk_config config = { .ini = NULL, @@ -172,6 +124,11 @@ int main(int argc, char *argv[]){ }; mk_env_init(&config); + if (argc != 3) { + printf("Usage: ./pusher.c pull_url push_url\n"); + return -1; + } + //可以通过 //rtmp://127.0.0.1/live/test //rtsp://127.0.0.1/live/test @@ -179,20 +136,14 @@ int main(int argc, char *argv[]){ mk_rtsp_server_start(554, 0); mk_rtmp_server_start(1935, 0); - Context *ctx = (Context *)malloc(sizeof(Context)); + Context *ctx = (Context *) malloc(sizeof(Context)); memset(ctx, 0, sizeof(Context)); //推流给自己测试,当然也可以推流给其他服务器测试 - context_start(ctx, "http://hls.weathertv.cn/tslslive/qCFIfHB/hls/live_sd.m3u8", "rtsp://127.0.0.1/live/rtsp_push"); + context_start(ctx, argv[1], argv[2]); - int i = 10 * 60; - while(--i){ -#ifdef _WIN32 - Sleep(1000); -#else - sleep(1); -#endif - } + log_info("enter any key to exit"); + getchar(); release_context(&ctx); return 0; diff --git a/api/tests/server.c b/api/tests/server.c index 922e19ee..cd400fd4 100644 --- a/api/tests/server.c +++ b/api/tests/server.c @@ -8,18 +8,8 @@ * may be found in the AUTHORS file in the root of the source tree. */ -#include #include #include "mk_mediakit.h" - -#ifdef _WIN32 -#include "windows.h" -#else - -#include "unistd.h" - -#endif - #define LOG_LEV 4 /** @@ -396,10 +386,6 @@ void API_CALL on_mk_flow_report(const mk_media_info url_info, (int)mk_sock_info_peer_port(sender)); } -static int flag = 1; -static void s_on_exit(int sig){ - flag = 0; -} int main(int argc, char *argv[]) { char *ini_path = mk_util_get_exe_dir("c_api.ini"); char *ssl_path = mk_util_get_exe_dir("ssl.p12"); @@ -444,17 +430,11 @@ int main(int argc, char *argv[]) { .on_mk_flow_report = on_mk_flow_report }; mk_events_listen(&events); - log_info("media server %s", "stared!"); - signal(SIGINT, s_on_exit );// 设置退出信号 - while (flag) { -#ifdef _WIN32 - Sleep(1000); -#else - sleep(1); -#endif - } + log_info("enter any key to exit"); + getchar(); + mk_stop_all_server(); return 0; } \ No newline at end of file diff --git a/api/tests/websocket.c b/api/tests/websocket.c index 0c6113ac..54335046 100644 --- a/api/tests/websocket.c +++ b/api/tests/websocket.c @@ -8,26 +8,15 @@ * may be found in the AUTHORS file in the root of the source tree. */ -#include #include #include #include #include "mk_mediakit.h" -#ifdef _WIN32 -#include "windows.h" -#else -#include "unistd.h" -#endif #define LOG_LEV 4 //修改此宏,可以选择协议类型 #define TCP_TYPE mk_type_ws -static int flag = 1; -static void s_on_exit(int sig){ - flag = 0; -} - //////////////////////////////////////////////////////////////////////////////////////////////////////////// typedef struct { mk_tcp_session _session; @@ -52,11 +41,15 @@ void API_CALL on_mk_tcp_session_create(uint16_t server_port,mk_tcp_session sessi * @param data 数据指针 * @param len 数据长度 */ -void API_CALL on_mk_tcp_session_data(uint16_t server_port,mk_tcp_session session,const char *data,size_t len){ +void API_CALL on_mk_tcp_session_data(uint16_t server_port,mk_tcp_session session, mk_buffer buffer){ char ip[64]; - log_printf(LOG_LEV,"from %s %d, data[%d]: %s",mk_tcp_session_peer_ip(session,ip),(int)mk_tcp_session_peer_port(session), len,data); + log_printf(LOG_LEV,"from %s %d, data[%d]: %s", + mk_tcp_session_peer_ip(session,ip), + (int)mk_tcp_session_peer_port(session), + mk_buffer_get_size(buffer), + mk_buffer_get_data(buffer)); mk_tcp_session_send(session,"echo:",0); - mk_tcp_session_send(session,data,len); + mk_tcp_session_send_buffer(session, buffer); } /** @@ -129,8 +122,8 @@ void API_CALL on_mk_tcp_client_disconnect(mk_tcp_client client,int code,const ch * @param data 数据指针 * @param len 数据长度 */ -void API_CALL on_mk_tcp_client_data(mk_tcp_client client,const char *data,size_t len){ - log_printf(LOG_LEV,"data[%d]:%s",len,data); +void API_CALL on_mk_tcp_client_data(mk_tcp_client client, mk_buffer buffer){ + log_printf(LOG_LEV, "data[%d]:%s", mk_buffer_get_size(buffer), mk_buffer_get_data(buffer)); } /** @@ -203,14 +196,9 @@ int main(int argc, char *argv[]) { test_server(); test_client(); - signal(SIGINT, s_on_exit );// 设置退出信号 - while (flag) { -#ifdef _WIN32 - Sleep(1000); -#else - sleep(1); -#endif - } + log_info("enter any key to exit"); + getchar(); + mk_stop_all_server(); return 0; } \ No newline at end of file diff --git a/src/Extension/Frame.cpp b/src/Extension/Frame.cpp index d8997dfd..e0f0eb14 100644 --- a/src/Extension/Frame.cpp +++ b/src/Extension/Frame.cpp @@ -91,6 +91,9 @@ bool FrameMerger::willFlush(const Frame::Ptr &frame) const{ //缓存为空 return false; } + if (!frame) { + return true; + } switch (_type) { case none : { //frame不是完整的帧,我们合并为一帧 @@ -180,6 +183,10 @@ bool FrameMerger::inputFrame(const Frame::Ptr &frame, const onOutput &cb, Buffer _have_decode_able_frame = false; } + if (!frame) { + return false; + } + if (frame->decodeAble()) { _have_decode_able_frame = true; } diff --git a/src/Extension/Frame.h b/src/Extension/Frame.h index 4e7d7015..3fb26a11 100644 --- a/src/Extension/Frame.h +++ b/src/Extension/Frame.h @@ -310,45 +310,33 @@ private: */ class FrameDispatcher : public FrameWriterInterface { public: - typedef std::shared_ptr Ptr; - - FrameDispatcher(){} - virtual ~FrameDispatcher(){} + using Ptr = std::shared_ptr; + FrameDispatcher() = default; + ~FrameDispatcher() override = default; /** * 添加代理 */ - void addDelegate(const FrameWriterInterface::Ptr &delegate){ - //_delegates_write可能多线程同时操作 + void addDelegate(const FrameWriterInterface::Ptr &delegate) { std::lock_guard lck(_mtx); - _delegates_write.emplace(delegate.get(),delegate); - _need_update = true; + _delegates.emplace(delegate.get(), delegate); } /** * 删除代理 */ - void delDelegate(FrameWriterInterface *ptr){ - //_delegates_write可能多线程同时操作 + void delDelegate(FrameWriterInterface *ptr) { std::lock_guard lck(_mtx); - _delegates_write.erase(ptr); - _need_update = true; + _delegates.erase(ptr); } /** * 写入帧并派发 */ - bool inputFrame(const Frame::Ptr &frame) override{ - if(_need_update){ - //发现代理列表发生变化了,这里同步一次 - std::lock_guard lck(_mtx); - _delegates_read = _delegates_write; - _need_update = false; - } - - //_delegates_read能确保是单线程操作的 + bool inputFrame(const Frame::Ptr &frame) override { + std::lock_guard lck(_mtx); bool ret = false; - for (auto &pr : _delegates_read) { + for (auto &pr : _delegates) { if (pr.second->inputFrame(frame)) { ret = true; } @@ -360,13 +348,18 @@ public: * 返回代理个数 */ size_t size() const { - return _delegates_write.size(); + std::lock_guard lck(_mtx); + return _delegates.size(); } + + void clear() { + std::lock_guard lck(_mtx); + _delegates.clear(); + } + private: - std::mutex _mtx; - std::map _delegates_read; - std::map _delegates_write; - bool _need_update = false; + mutable std::mutex _mtx; + std::map _delegates; }; /**