2024-04-05 22:07:09 +08:00
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
|
|
|
|
|
*
|
|
|
|
|
* Use of this source code is governed by MIT-like 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 <signal.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
#include "windows.h"
|
|
|
|
|
#else
|
|
|
|
|
#include "unistd.h"
|
|
|
|
|
#endif
|
|
|
|
|
#include "mk_mediakit.h"
|
|
|
|
|
|
|
|
|
|
static int exit_flag = 0;
|
|
|
|
|
static void s_on_exit(int sig) {
|
|
|
|
|
exit_flag = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
mk_pusher pusher;
|
|
|
|
|
char *url;
|
|
|
|
|
} Context;
|
|
|
|
|
|
|
|
|
|
void release_context(void *user_data) {
|
|
|
|
|
Context *ptr = (Context *)user_data;
|
|
|
|
|
if (ptr->pusher) {
|
|
|
|
|
mk_pusher_release(ptr->pusher);
|
|
|
|
|
}
|
|
|
|
|
free(ptr->url);
|
|
|
|
|
free(ptr);
|
|
|
|
|
log_info("停止推流");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void on_push_result(void *user_data, int err_code, const char *err_msg) {
|
|
|
|
|
Context *ptr = (Context *)user_data;
|
|
|
|
|
if (err_code == 0) {
|
|
|
|
|
log_info("推流成功: %s", ptr->url);
|
|
|
|
|
} else {
|
|
|
|
|
log_warn("推流%s失败: %d(%s)", ptr->url, err_code, err_msg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void on_push_shutdown(void *user_data, int err_code, const char *err_msg) {
|
|
|
|
|
Context *ptr = (Context *)user_data;
|
|
|
|
|
log_warn("推流%s中断: %d(%s)", ptr->url, err_code, err_msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void API_CALL on_regist(void *user_data, mk_media_source sender, int regist) {
|
|
|
|
|
Context *ptr = (Context *)user_data;
|
|
|
|
|
const char *schema = mk_media_source_get_schema(sender);
|
2024-04-08 10:16:45 +08:00
|
|
|
|
if (strstr(ptr->url, schema) != ptr->url) {
|
2024-09-19 14:53:50 +08:00
|
|
|
|
// 协议匹配失败 [AUTO-TRANSLATED:436784d0]
|
|
|
|
|
// Protocol matching failed
|
2024-04-05 22:07:09 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!regist) {
|
2024-09-19 14:53:50 +08:00
|
|
|
|
// 注销 [AUTO-TRANSLATED:ebc5be28]
|
|
|
|
|
// Log out
|
2024-04-05 22:07:09 +08:00
|
|
|
|
if (ptr->pusher) {
|
|
|
|
|
mk_pusher_release(ptr->pusher);
|
|
|
|
|
ptr->pusher = NULL;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2024-09-19 14:53:50 +08:00
|
|
|
|
// 注册 [AUTO-TRANSLATED:e2df30a6]
|
|
|
|
|
// Register
|
2024-04-05 22:07:09 +08:00
|
|
|
|
if (!ptr->pusher) {
|
|
|
|
|
ptr->pusher = mk_pusher_create_src(sender);
|
|
|
|
|
mk_pusher_set_on_result2(ptr->pusher, on_push_result, ptr, NULL);
|
|
|
|
|
mk_pusher_set_on_shutdown2(ptr->pusher, on_push_shutdown, ptr, NULL);
|
2024-09-19 14:53:50 +08:00
|
|
|
|
// 开始推流 [AUTO-TRANSLATED:df3972ff]
|
|
|
|
|
// Start streaming
|
2024-04-05 22:07:09 +08:00
|
|
|
|
mk_pusher_publish(ptr->pusher, ptr->url);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
|
|
|
if (argc < 3) {
|
|
|
|
|
log_error("Usage: /path/to/h264/file rtsp_or_rtmp_url");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
mk_config config = { .ini = NULL,
|
|
|
|
|
.ini_is_path = 1,
|
|
|
|
|
.log_level = 0,
|
|
|
|
|
.log_mask = LOG_CONSOLE,
|
|
|
|
|
.log_file_path = NULL,
|
|
|
|
|
.log_file_days = 0,
|
|
|
|
|
.ssl = NULL,
|
|
|
|
|
.ssl_is_path = 1,
|
|
|
|
|
.ssl_pwd = NULL,
|
|
|
|
|
.thread_num = 0 };
|
|
|
|
|
mk_env_init(&config);
|
|
|
|
|
|
|
|
|
|
FILE *fp = fopen(argv[1], "rb");
|
|
|
|
|
if (!fp) {
|
|
|
|
|
log_error("打开文件失败!");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mk_media media = mk_media_create("__defaultVhost__", "live", "test", 0, 0, 0);
|
2024-09-19 14:53:50 +08:00
|
|
|
|
// h264的codec [AUTO-TRANSLATED:e840179e]
|
|
|
|
|
// h264 codec
|
2024-04-05 22:07:09 +08:00
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
Context *ctx = (Context *)malloc(sizeof(Context));
|
|
|
|
|
memset(ctx, 0, sizeof(Context));
|
|
|
|
|
ctx->url = strdup(argv[2]);
|
|
|
|
|
|
|
|
|
|
mk_media_set_on_regist2(media, on_regist, ctx, release_context);
|
|
|
|
|
|
2024-09-19 14:53:50 +08:00
|
|
|
|
// 创建h264分帧器 [AUTO-TRANSLATED:72254159]
|
|
|
|
|
// Create h264 frame splitter
|
2024-04-05 22:07:09 +08:00
|
|
|
|
mk_h264_splitter splitter = mk_h264_splitter_create(on_h264_frame, media, 0);
|
|
|
|
|
signal(SIGINT, s_on_exit); // 设置退出信号
|
|
|
|
|
signal(SIGTERM, s_on_exit); // 设置退出信号
|
|
|
|
|
|
|
|
|
|
char buf[1024];
|
|
|
|
|
while (!exit_flag) {
|
|
|
|
|
int size = fread(buf, 1, sizeof(buf) - 1, fp);
|
|
|
|
|
if (size > 0) {
|
|
|
|
|
mk_h264_splitter_input_data(splitter, buf, size);
|
|
|
|
|
} else {
|
2024-09-19 14:53:50 +08:00
|
|
|
|
// 文件读完了,重新开始 [AUTO-TRANSLATED:ffffe75c]
|
|
|
|
|
// File read finished, start again
|
2024-04-05 22:07:09 +08:00
|
|
|
|
fseek(fp, 0, SEEK_SET);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log_info("文件读取完毕");
|
|
|
|
|
mk_h264_splitter_release(splitter);
|
|
|
|
|
mk_media_release(media);
|
|
|
|
|
fclose(fp);
|
|
|
|
|
return 0;
|
2024-04-08 10:16:45 +08:00
|
|
|
|
}
|