remux.c 3.58 KB
Newer Older
Julian Rother's avatar
Julian Rother committed
1
2
3
4
5
6
7
8
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>

#include "util.h"
#include "config.h"

static AVRational chapter_time_base = {1, 1};

void parse_dict(AVDictionary **d, char *s)
{
	char key[JSTR_SIZE], value[JSTR_SIZE];
	for (s = jenter(s); s; s = jnext(s))
		av_dict_set(d, jstrb(s, 0, key), jstrb(jvalue(s), 0, value), 0);
}

void parse_chapters(AVFormatContext *ctx, char *s, int duration)
{
	char *p;
	int i;
	for (p = jenter(s), i = 0; p; p = jnext(p), i ++);
	ctx->chapters = malloc(sizeof(AVChapter *)*i);
	ctx->nb_chapters = i;
	for (p = jenter(s), i = 0; p; p = jnext(p), i ++)
	{
		ctx->chapters[i] = malloc(sizeof(AVChapter));
		memset(ctx->chapters[i], 0, sizeof(AVChapter));
		ctx->chapters[i]->id = i;
		ctx->chapters[i]->time_base = chapter_time_base;
		ctx->chapters[i]->start = jint(jlookup(p, "time"), 0);
		av_dict_set(&ctx->chapters[i]->metadata, "title", jstr(jlookup(p, "text"), "EMPTY"), 0);
		if (i)
			ctx->chapters[i-1]->end = ctx->chapters[i]->start;
	}
	ctx->chapters[i-1]->end = duration;
}

int main(int argc, char *argv[])
{
	int jobid, i;
	int *idxmap;
	char *p, path[100], tmp[100];
	AVFormatContext *demux, *mux;
	AVPacket pkt;
	AVStream *stream;
	if (argc != 5)
		return 1;
	av_register_all();
	av_log_set_callback(avlogbuf_callback);
	memset(&pkt, 0, sizeof(pkt));
	av_init_packet(&pkt);

	jobid = atoi(argv[1]);
	xsnprintf(BL(path), "%s/%s", CONFIG_VIDEOS_RELEASED,
			jstr(jlookup(argv[4], "path"), ""));
	xsnprintf(BL(tmp), "%s/.tmp-%i", CONFIG_VIDEOS_TMP, jobid);
	ping_job(jobid, "running", 0);

	demux = 0;
	if (avformat_open_input(&demux, path, 0, 0))
		goto fail;
	avformat_find_stream_info(demux, 0);
	avformat_alloc_output_context2(&mux, 0, 0, path);
	parse_dict(&mux->metadata, jlookup(argv[4], "metadata"));
	parse_chapters(mux, jlookup(argv[4], "chapters"), av_rescale_q(demux->duration, AV_TIME_BASE_Q, chapter_time_base));
	idxmap = malloc(sizeof(int)*demux->nb_streams);
	for (i = 0; i < demux->nb_streams; i ++)
	{
		if (demux->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO
				&& demux->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO
				&& demux->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
		{
			idxmap[i] = -1;
			continue;
		}
		stream = avformat_new_stream(mux, 0);
		idxmap[i] = stream->index;
		avcodec_parameters_copy(mux->streams[i]->codecpar,
				demux->streams[i]->codecpar);
		mux->streams[i]->codecpar->codec_tag = 0;
		mux->streams[i]->time_base = demux->streams[i]->time_base;
	}
	avio_open(&mux->pb, tmp, AVIO_FLAG_WRITE);
	if (avformat_write_header(mux, 0) < 0)
		goto fail;
	while (!av_read_frame(demux, &pkt))
	{
		if (pkt.stream_index >= demux->nb_streams
				|| idxmap[pkt.stream_index] == -1)
			continue;
		pkt.stream_index = idxmap[pkt.stream_index];
		pkt.pts = av_rescale_q_rnd(pkt.pts, demux->streams[pkt.stream_index]->time_base,
				mux->streams[pkt.stream_index]->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
		pkt.dts = av_rescale_q_rnd(pkt.dts, demux->streams[pkt.stream_index]->time_base,
				mux->streams[pkt.stream_index]->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
		pkt.duration = av_rescale_q(pkt.duration, demux->streams[pkt.stream_index]->time_base,
				mux->streams[pkt.stream_index]->time_base);
		pkt.pos = -1;
		if (av_interleaved_write_frame(mux, &pkt))
			goto fail;
	}
	av_interleaved_write_frame(mux, 0);
	if (av_write_trailer(mux))
		goto fail;
	avio_closep(&mux->pb);
	if (rename(tmp, path))
		goto fail;
	unlink(tmp);
	ping_job(jobid, "finished", "{\"log\": \"%s\"}", get_avlogbuf());
	return 0;

fail:
	unlink(tmp);
	ping_job(jobid, "failed", "{\"log\": \"%s\"}", get_avlogbuf());
	return 1;
}