complex_live_transcode.c 3.29 KB
Newer Older
1 2 3 4 5 6 7
#include <poll.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "util.h"

8
#define OUTPUT_FORMAT(bitrate) "-f", "flv", "-r", "25", "-c:v", "libx264", "-b:v", bitrate, "-bufsize", bitrate, "-preset", "veryfast", "-x264-params", "keyint=125:min-keyint=125:no-scenecut", "-c:a", "aac"
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

static void copy_to_log(int fd)
{
	static char buf[1024];
	size_t len;
	if ((len = read(fd, BL(buf))) > 0)
		fwrite(buf, 1, len, logstream);
}

int main(int argc, char *argv[])
{
	int ret, canceled = 0, errpipe[2] = {-1, -1};
	char *src1, *src2, *src1fmt = "flv", *src2fmt = "flv", *dest, *logo_expr;
	pid_t pid;
	struct pollfd fds = {.fd = -1, .events = POLLIN};
	if (argc != 5)
		return 1;
	init_env();
	init_avlogbuf();
	jobid = atoi(argv[1]);
	src1 = jlookup(argv[4], "src1");
	src2 = jlookup(argv[4], "src2");
	if (!jstr(jlookup(src1, "url"), 0))
		src1fmt = "lavfi";
	if (!jstr(jlookup(src2, "url"), 0))
		src2fmt = "lavfi";
	dest = jstr(jlookup(argv[4], "destbase"), "rtmp://127.0.0.1:999999/nourl");
	if (jint(jlookup(argv[4], "videoag_logo"), 1))
		logo_expr = "overlay=x=W-195:y=H-155";
	else
		logo_expr = "overlay=x=W+1000:y=H+1000";

	pipe(errpipe);
	if (!(pid = fork()))
	{
		close(errpipe[0]);
		dup2(errpipe[1], 2); close(errpipe[1]);
		dup2(open("/dev/null", O_RDONLY|O_CLOEXEC), 0);
		dup2(open("/dev/null", O_WRONLY|O_CLOEXEC), 1);
		execlp("ffmpeg", "ffmpeg", "-nostats",
49 50
				"-f", src1fmt, "-i", jstr(jlookup(src1, "url"), "smptehdbars,scale=1920:1080[out0]; anullsrc[out1]"),
				"-f", src2fmt, "-i", jstr(jlookup(src2, "url"), "smptehdbars,scale=1920:1080[out0]; anullsrc[out1]"),
51
				"-filter_complex",
52
				mprintf("[0:a]%s[a1]; [1:a]%s[a2]; [a1][a2]amix,%s,asplit=3[lowa][mida][higha];"
53 54
					"[0:v]%s[v1]; [1:v]%s[v2]; [v1][v2]%s,scale=1920:1080[tmp];"
					"movie=%s/video-logo-150px-trans.png,scale=w=100:h=100[logo];"
55
					"[tmp][logo]%s,split=3[lowtmp][midtmp][high];"
56 57
					"[lowtmp]scale=640:360[low];"
					"[midtmp]scale=1280:720[mid]",
58 59 60 61 62
					jstr(jlookup(src1, "afilter"), "anull"),
					jstr(jlookup(src2, "afilter"), "anull"),
					jstr(jlookup(argv[4], "afilter"), "anull"),
					jstr(jlookup(src1, "vfilter"), "null"),
					jstr(jlookup(src2, "vfilter"), "null"),
63 64 65 66
					jstr(jlookup(argv[4], "vmix"), "streamselect=map=0"),
					getenv(WORKER_RAW), logo_expr
				),
				"-map", "[low]", "-map", "[lowa]", OUTPUT_FORMAT("512k"), mprintf("%s_low", dest),
67 68
				"-map", "[mid]", "-map", "[mida]", OUTPUT_FORMAT("2024k"), mprintf("%s_mid", dest),
				"-map", "[high]", "-map", "[higha]", OUTPUT_FORMAT("6024k"), mprintf("%s_high", dest), 0);
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
		exit(1);
	}
	close(errpipe[1]);
	fds.fd = errpipe[0];

	while (1)
	{
		canceled = ping_job(jobid, "running", "{\"log\": \"%s\"}", jescape(get_avlogbuf()));
		if (canceled == 1)
			kill(pid, SIGINT); /* Stop ffmpeg */
		poll(&fds, 1, 15000);
		if (fds.revents & POLLIN)
			copy_to_log(fds.fd);
		else if (fds.revents & (POLLERR|POLLHUP|POLLNVAL))
			break;
	}

	waitpid(pid, &ret, 0);
	if (canceled == 1 && (WEXITSTATUS(ret) == 255 || (WIFSIGNALED(ret) && WTERMSIG(ret) == SIGINT)))
		ping_job(jobid, "finished", "{\"log\": \"%s\"}", jescape(get_avlogbuf()));
	else if (WIFSIGNALED(ret))
		job_failed("Subprocesses was killed by signal %s (%i)", strsignal(WTERMSIG(ret)), WTERMSIG(ret));
	else
		job_failed("Subprocesses terminated with exit status %i", WEXITSTATUS(ret));
	return 0;
}