-
-
Save md2k/e2768044d2737e4cfa3d536b8f0ec64e to your computer and use it in GitHub Desktop.
A simple service to loop RTMP VOD streaming
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* loop_rtmp.c: A simple service to loop RTMP VOD streaming | |
* | |
* Usage: | |
* loop_rtmp <playlist> <channel> | |
* | |
*/ | |
#include <unistd.h> | |
#include <stdint.h> | |
#include <inttypes.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <signal.h> | |
#include <sys/wait.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include "rtmp_config.h" | |
#include "sys_utils.h" | |
#include "task_exec.h" | |
#define LOCAL_LOG_IDENTIFY "loop_rtmp/loop_rtmp" | |
static int check_playlist(const char *playlist) | |
{ | |
FILE *fp = fopen(playlist, "r"); | |
char *buffer,*p, *ptr; | |
char line[2048] = {0}; | |
char filename[2048]; | |
int total_size = 1024*2048; | |
int len = 0; | |
struct stat file_stat; | |
if (!fp) | |
return -1; | |
buffer = malloc(total_size); | |
if (!buffer) { | |
fclose(fp); | |
return -2; | |
} | |
p = buffer; | |
// | |
// the playlist format: | |
// .... | |
// file /main_disk/live_record/imbatv_1500K-1422099849.mp4 | |
// file /main_disk/live_record/imbatv_1500K-1422171768.mp4 | |
// .... | |
// | |
while (fgets(line, sizeof(line) - 1 , fp) != NULL) { | |
// Only work for *nix | |
ptr = strchr(line, '/'); | |
if (ptr) { | |
strcpy(filename, ptr); | |
ptr = filename; | |
while(*ptr) { | |
// remove tail "\n" or "\r" | |
if (*ptr == '\n' || *ptr == '\r') { | |
*ptr = '\0'; | |
break; | |
} | |
ptr++; | |
} | |
// check if file is available | |
// MUST check the path is a regular file, not a directory | |
if (access(filename, F_OK) != -1 && | |
(stat(filename, &file_stat) == 0 && S_ISREG(file_stat.st_mode))) { | |
int n = strlen(line); | |
if (len + n > total_size) | |
break; | |
strncpy(p, line, n); | |
len += n; | |
p += n; | |
} | |
} | |
} | |
fclose(fp); | |
fp = fopen(playlist, "w"); | |
if (fp) { | |
fwrite(buffer, 1, len, fp); | |
fclose(fp); | |
} | |
free(buffer); | |
return 0; | |
} | |
// | |
// The playlist is published to "rtmp://127.0.0.1/vod/$channel" | |
// | |
int main(int argc, const char *argv[]) | |
{ | |
pid_t pid = 0; | |
char input_url[MAX_URL_LENGTH] = {0}; | |
char rtmp_url[MAX_URL_LENGTH] = {0}; | |
char channel[128] = {0}; | |
char logfile[256] = {0}; | |
char cmd[2048] = {0}; | |
int i=0, max_loop_count = 256; | |
if (argc != 3) { | |
fprintf(stderr, "Usage: loop_rtmp <playlist> <channel>\n"); | |
exit(1); | |
} | |
snprintf(channel, sizeof(channel), "%s", argv[2]); | |
ptr = strstr(channel, PLATFORM_MAGIC_CODE); | |
if (!ptr) { | |
fprintf(stderr, "Channel code(%s) is wrong\n", channel); | |
return -1; | |
} | |
*ptr = '\0'; | |
snprintf(input_url, MAX_URL_LENGTH, "%s", argv[1]); | |
snprintf(rtmp_url, MAX_URL_LENGTH, "rtmp://127.0.0.1/vod/%s", channel); | |
fprintf(stderr, "input_url: %s\n", input_url); | |
fprintf(stderr, "loop_channel: %s\n", rtmp_url); | |
fprintf(stderr, "\n"); | |
while (i < max_loop_count) { | |
// | |
// kill all subscribers which are listening to current channel | |
// otherwise, the timestamp is wrong when re-loop, we will get the following huge warnings: | |
// | |
// DTS 4203501128, next:2082712000 st:0 invalid dropping | |
// PTS 4203501208, next:2082712000 invalid dropping st:0 | |
// DTS 4203501168, next:2082752000 st:0 invalid dropping | |
// PTS 4203501368, next:2082752000 invalid dropping st:0 | |
// .... | |
// | |
// until the remote publish server close client's connection | |
// | |
// Another solution: repeat/extend contents in the playlist to 21 days | |
// | |
kill_apps(FFMPEG_APP, rtmp_url); | |
make_log_name(LOG_BASE_PATH, LOCAL_LOG_IDENTIFY, channel, logfile, sizeof(logfile)); | |
// | |
// Make sure the files in playlist are available, otherwise, we will get the following error: | |
// | |
// [concat @ 0x29f60c0] Impossible to open '/main_disk/live_record/imbatv_1500K-1421920601.mp4' | |
// /main_disk/logs/mp4_1500K_playlist.txt: No such file or directory | |
// | |
check_playlist(input_url); | |
snprintf(cmd, sizeof(cmd), "/usr/local/bin/%s -loglevel warning -xerror -re -f concat -i '%s' -c copy -metadata title='loop_%s' -f flv '%s' &> '%s'", FFMPEG_APP, inp | |
ut_url, argv[2], rtmp_url, logfile); | |
fprintf(stderr, "ffmpeg_cmd: %s\n", cmd); | |
pid = task_exec(cmd, 0); | |
// TODO: playlist refresh automatically | |
waitpid(pid, &status, 0); | |
// can not capture: SIGKILL and SIGSTOP | |
if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) { | |
fprintf(stderr, "terminated forcely"); | |
break; | |
} | |
i++; | |
// Must sleep, because playlist list is a local file which will not be blocked by network IO | |
sleep(1); | |
fprintf(stderr, "\n***** loop playlist: count=%d *****\n", i); | |
} | |
fprintf(stderr, "The App has been terminated: loop_count=%d\n", i); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment