#include #include #include "util.h" #include "config.h" static AVRational chapter_time_base = {1, 1}; void parse_dict(AVDictionary **d, char *s) { char *val; char key[JSTR_SIZE], value[JSTR_SIZE]; for (s = jenter(s); s; s = jnext(s)) { if (val = jstrb(jvalue(s), 0, value)) av_dict_set(d, jstrb(s, 0, key), val, 0); else av_dict_set_int(d, jstrb(s, 0, key), jint(jvalue(s), 0), 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; AVDictionary *muxopts; 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, jstr(jlookup(argv[4], "format"), 0), path); av_dict_copy(&mux->metadata, demux->metadata, 0); 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 ++) { idxmap[i] = -1; 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) continue; stream = avformat_new_stream(mux, 0); av_dict_copy(&stream->metadata, mux->streams[i]->metadata, 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); muxopts = 0; parse_dict(&muxopts, jlookup(argv[4], "options")); if (avformat_write_header(mux, &muxopts) < 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; }