static int mkv_write_header(AVFormatContext *s) { MatroskaMuxContext *mkv = s->priv_data; AVIOContext *pb = s->pb; ebml_master ebml_header; AVDictionaryEntry *tag; int ret, i, version = 2; int64_t creation_time; if (!strcmp(s->oformat->name, "webm")) mkv->mode = MODE_WEBM; else mkv->mode = MODE_MATROSKAv2; if (mkv->mode != MODE_WEBM || av_dict_get(s->metadata, "stereo_mode", NULL, 0) || av_dict_get(s->metadata, "alpha_mode", NULL, 0)) version = 4; if (s->nb_streams > MAX_TRACKS) { av_log(s, AV_LOG_ERROR, "At most %d streams are supported for muxing in Matroska\n", MAX_TRACKS); return AVERROR(EINVAL); } for (i = 0; i < s->nb_streams; i++) { if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_ATRAC3 || s->streams[i]->codecpar->codec_id == AV_CODEC_ID_COOK || s->streams[i]->codecpar->codec_id == AV_CODEC_ID_RA_288 || s->streams[i]->codecpar->codec_id == AV_CODEC_ID_SIPR || s->streams[i]->codecpar->codec_id == AV_CODEC_ID_RV10 || s->streams[i]->codecpar->codec_id == AV_CODEC_ID_RV20) { av_log(s, AV_LOG_ERROR, "The Matroska muxer does not yet support muxing %s\n", avcodec_get_name(s->streams[i]->codecpar->codec_id)); return AVERROR_PATCHWELCOME; } if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_OPUS || av_dict_get(s->streams[i]->metadata, "stereo_mode", NULL, 0) || av_dict_get(s->streams[i]->metadata, "alpha_mode", NULL, 0)) version = 4; } mkv->tracks = av_mallocz_array(s->nb_streams, sizeof(*mkv->tracks)); if (!mkv->tracks) { ret = AVERROR(ENOMEM); goto fail; } ebml_header = start_ebml_master(pb, EBML_ID_HEADER, 0); put_ebml_uint (pb, EBML_ID_EBMLVERSION , 1); put_ebml_uint (pb, EBML_ID_EBMLREADVERSION , 1); put_ebml_uint (pb, EBML_ID_EBMLMAXIDLENGTH , 4); put_ebml_uint (pb, EBML_ID_EBMLMAXSIZELENGTH , 8); put_ebml_string (pb, EBML_ID_DOCTYPE , s->oformat->name); put_ebml_uint (pb, EBML_ID_DOCTYPEVERSION , version); put_ebml_uint (pb, EBML_ID_DOCTYPEREADVERSION , 2); end_ebml_master(pb, ebml_header); mkv->segment = start_ebml_master(pb, MATROSKA_ID_SEGMENT, 0); mkv->segment_offset = avio_tell(pb); // we write 2 seek heads - one at the end of the file to point to each // cluster, and one at the beginning to point to all other level one // elements (including the seek head at the end of the file), which // isn't more than 10 elements if we only write one of each other // currently defined level 1 element mkv->main_seekhead = mkv_start_seekhead(pb, mkv->segment_offset, 10); if (!mkv->main_seekhead) { ret = AVERROR(ENOMEM); goto fail; } ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_INFO, avio_tell(pb)); if (ret < 0) goto fail; ret = start_ebml_master_crc32(pb, &mkv->info_bc, mkv, &mkv->info, MATROSKA_ID_INFO, 0); if (ret < 0) return ret; pb = mkv->info_bc; put_ebml_uint(pb, MATROSKA_ID_TIMECODESCALE, 1000000); if ((tag = av_dict_get(s->metadata, "title", NULL, 0))) put_ebml_string(pb, MATROSKA_ID_TITLE, tag->value); if (!(s->flags & AVFMT_FLAG_BITEXACT)) { put_ebml_string(pb, MATROSKA_ID_MUXINGAPP, LIBAVFORMAT_IDENT); if ((tag = av_dict_get(s->metadata, "encoding_tool", NULL, 0))) put_ebml_string(pb, MATROSKA_ID_WRITINGAPP, tag->value); else put_ebml_string(pb, MATROSKA_ID_WRITINGAPP, LIBAVFORMAT_IDENT); if (mkv->mode != MODE_WEBM) { uint32_t segment_uid[4]; AVLFG lfg; av_lfg_init(&lfg, av_get_random_seed()); for (i = 0; i < 4; i++) segment_uid[i] = av_lfg_get(&lfg); put_ebml_binary(pb, MATROSKA_ID_SEGMENTUID, segment_uid, 16); } } else { const char *ident = "Lavf"; put_ebml_string(pb, MATROSKA_ID_MUXINGAPP , ident); put_ebml_string(pb, MATROSKA_ID_WRITINGAPP, ident); } if (ff_parse_creation_time_metadata(s, &creation_time, 0) > 0) { // Adjust time so it's relative to 2001-01-01 and convert to nanoseconds. int64_t date_utc = (creation_time - 978307200000000LL) * 1000; uint8_t date_utc_buf[8]; AV_WB64(date_utc_buf, date_utc); put_ebml_binary(pb, MATROSKA_ID_DATEUTC, date_utc_buf, 8); } // reserve space for the duration mkv->duration = 0; mkv->duration_offset = avio_tell(pb); if (!mkv->is_live) { int64_t metadata_duration = get_metadata_duration(s); if (s->duration > 0) { int64_t scaledDuration = av_rescale(s->duration, 1000, AV_TIME_BASE); put_ebml_float(pb, MATROSKA_ID_DURATION, scaledDuration); av_log(s, AV_LOG_DEBUG, "Write early duration from recording time = %" PRIu64 "\n", scaledDuration); } else if (metadata_duration > 0) { int64_t scaledDuration = av_rescale(metadata_duration, 1000, AV_TIME_BASE); put_ebml_float(pb, MATROSKA_ID_DURATION, scaledDuration); av_log(s, AV_LOG_DEBUG, "Write early duration from metadata = %" PRIu64 "\n", scaledDuration); } else { put_ebml_void(pb, 11); // assumes double-precision float to be written } } if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) end_ebml_master_crc32_preliminary(s->pb, &mkv->info_bc, mkv, mkv->info); else end_ebml_master_crc32(s->pb, &mkv->info_bc, mkv, mkv->info); pb = s->pb; // initialize stream_duration fields mkv->stream_durations = av_mallocz(s->nb_streams * sizeof(int64_t)); mkv->stream_duration_offsets = av_mallocz(s->nb_streams * sizeof(int64_t)); ret = mkv_write_tracks(s); if (ret < 0) goto fail; for (i = 0; i < s->nb_chapters; i++) mkv->chapter_id_offset = FFMAX(mkv->chapter_id_offset, 1LL - s->chapters[i]->id); ret = mkv_write_chapters(s); if (ret < 0) goto fail; if (mkv->mode != MODE_WEBM) { ret = mkv_write_attachments(s); if (ret < 0) goto fail; } ret = mkv_write_tags(s); if (ret < 0) goto fail; if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) mkv_write_seekhead(pb, mkv); mkv->cues = mkv_start_cues(mkv->segment_offset); if (!mkv->cues) { ret = AVERROR(ENOMEM); goto fail; } if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && mkv->reserve_cues_space) { mkv->cues_pos = avio_tell(pb); put_ebml_void(pb, mkv->reserve_cues_space); } av_init_packet(&mkv->cur_audio_pkt); mkv->cur_audio_pkt.size = 0; mkv->cluster_pos = -1; avio_flush(pb); // start a new cluster every 5 MB or 5 sec, or 32k / 1 sec for streaming or // after 4k and on a keyframe if (pb->seekable & AVIO_SEEKABLE_NORMAL) { if (mkv->cluster_time_limit < 0) mkv->cluster_time_limit = 5000; if (mkv->cluster_size_limit < 0) mkv->cluster_size_limit = 5 * 1024 * 1024; } else { if (mkv->cluster_time_limit < 0) mkv->cluster_time_limit = 1000; if (mkv->cluster_size_limit < 0) mkv->cluster_size_limit = 32 * 1024; } return 0; fail: mkv_free(mkv); return ret; }