| @@ -535,6 +535,42 @@ static void get_aac_sample_rates(AVFormatContext *s, AVCodecContext *codec, | |||
| *output_sample_rate = mp4ac.ext_sample_rate; | |||
| } | |||
| static int mkv_write_native_codecprivate(AVFormatContext *s, | |||
| AVCodecContext *codec, | |||
| AVIOContext *dyn_cp) | |||
| { | |||
| switch (codec->codec_id) { | |||
| case AV_CODEC_ID_VORBIS: | |||
| case AV_CODEC_ID_THEORA: | |||
| return put_xiph_codecpriv(s, dyn_cp, codec); | |||
| case AV_CODEC_ID_FLAC: | |||
| return put_flac_codecpriv(s, dyn_cp, codec); | |||
| case AV_CODEC_ID_WAVPACK: | |||
| return put_wv_codecpriv(dyn_cp, codec); | |||
| case AV_CODEC_ID_H264: | |||
| return ff_isom_write_avcc(dyn_cp, codec->extradata, | |||
| codec->extradata_size); | |||
| case AV_CODEC_ID_HEVC: | |||
| return ff_isom_write_hvcc(dyn_cp, codec->extradata, | |||
| codec->extradata_size, 0); | |||
| case AV_CODEC_ID_ALAC: | |||
| if (codec->extradata_size < 36) { | |||
| av_log(s, AV_LOG_ERROR, | |||
| "Invalid extradata found, ALAC expects a 36-byte " | |||
| "QuickTime atom."); | |||
| return AVERROR_INVALIDDATA; | |||
| } else | |||
| avio_write(dyn_cp, codec->extradata + 12, | |||
| codec->extradata_size - 12); | |||
| break; | |||
| default: | |||
| if (codec->extradata_size) | |||
| avio_write(dyn_cp, codec->extradata, codec->extradata_size); | |||
| } | |||
| return 0; | |||
| } | |||
| static int mkv_write_codecprivate(AVFormatContext *s, AVIOContext *pb, | |||
| AVCodecContext *codec, int native_id, | |||
| int qt_id) | |||
| @@ -548,30 +584,7 @@ static int mkv_write_codecprivate(AVFormatContext *s, AVIOContext *pb, | |||
| return ret; | |||
| if (native_id) { | |||
| if (codec->codec_id == AV_CODEC_ID_VORBIS || | |||
| codec->codec_id == AV_CODEC_ID_THEORA) | |||
| ret = put_xiph_codecpriv(s, dyn_cp, codec); | |||
| else if (codec->codec_id == AV_CODEC_ID_FLAC) | |||
| ret = put_flac_codecpriv(s, dyn_cp, codec); | |||
| else if (codec->codec_id == AV_CODEC_ID_WAVPACK) | |||
| ret = put_wv_codecpriv(dyn_cp, codec); | |||
| else if (codec->codec_id == AV_CODEC_ID_H264) | |||
| ret = ff_isom_write_avcc(dyn_cp, codec->extradata, | |||
| codec->extradata_size); | |||
| else if (codec->codec_id == AV_CODEC_ID_HEVC) | |||
| ret = ff_isom_write_hvcc(dyn_cp, codec->extradata, | |||
| codec->extradata_size, 0); | |||
| else if (codec->codec_id == AV_CODEC_ID_ALAC) { | |||
| if (codec->extradata_size < 36) { | |||
| av_log(s, AV_LOG_ERROR, | |||
| "Invalid extradata found, ALAC expects a 36-byte " | |||
| "QuickTime atom."); | |||
| ret = AVERROR_INVALIDDATA; | |||
| } else | |||
| avio_write(dyn_cp, codec->extradata + 12, | |||
| codec->extradata_size - 12); | |||
| } else if (codec->extradata_size) | |||
| avio_write(dyn_cp, codec->extradata, codec->extradata_size); | |||
| ret = mkv_write_native_codecprivate(s, codec, dyn_cp); | |||
| } else if (codec->codec_type == AVMEDIA_TYPE_VIDEO) { | |||
| if (qt_id) { | |||
| if (!codec->codec_tag) | |||
| @@ -611,174 +624,185 @@ static int mkv_write_codecprivate(AVFormatContext *s, AVIOContext *pb, | |||
| return ret; | |||
| } | |||
| static int mkv_write_tracks(AVFormatContext *s) | |||
| static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, | |||
| int i, AVIOContext *pb) | |||
| { | |||
| MatroskaMuxContext *mkv = s->priv_data; | |||
| AVIOContext *pb = s->pb; | |||
| ebml_master tracks; | |||
| int i, j, ret; | |||
| AVStream *st = s->streams[i]; | |||
| AVCodecContext *codec = st->codec; | |||
| ebml_master subinfo, track; | |||
| int native_id = 0; | |||
| int qt_id = 0; | |||
| int bit_depth = av_get_bits_per_sample(codec->codec_id); | |||
| int sample_rate = codec->sample_rate; | |||
| int output_sample_rate = 0; | |||
| int j, ret; | |||
| AVDictionaryEntry *tag; | |||
| ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_TRACKS, avio_tell(pb)); | |||
| if (ret < 0) | |||
| return ret; | |||
| // ms precision is the de-facto standard timescale for mkv files | |||
| avpriv_set_pts_info(st, 64, 1, 1000); | |||
| tracks = start_ebml_master(pb, MATROSKA_ID_TRACKS, 0); | |||
| for (i = 0; i < s->nb_streams; i++) { | |||
| AVStream *st = s->streams[i]; | |||
| AVCodecContext *codec = st->codec; | |||
| ebml_master subinfo, track; | |||
| int native_id = 0; | |||
| int qt_id = 0; | |||
| int bit_depth = av_get_bits_per_sample(codec->codec_id); | |||
| int sample_rate = codec->sample_rate; | |||
| int output_sample_rate = 0; | |||
| AVDictionaryEntry *tag; | |||
| // ms precision is the de-facto standard timescale for mkv files | |||
| avpriv_set_pts_info(st, 64, 1, 1000); | |||
| if (codec->codec_type == AVMEDIA_TYPE_ATTACHMENT) { | |||
| mkv->have_attachments = 1; | |||
| continue; | |||
| } | |||
| if (codec->codec_type == AVMEDIA_TYPE_ATTACHMENT) { | |||
| mkv->have_attachments = 1; | |||
| return 0; | |||
| } | |||
| if (!bit_depth) | |||
| bit_depth = av_get_bytes_per_sample(codec->sample_fmt) << 3; | |||
| if (!bit_depth) | |||
| bit_depth = av_get_bytes_per_sample(codec->sample_fmt) << 3; | |||
| if (codec->codec_id == AV_CODEC_ID_AAC) | |||
| get_aac_sample_rates(s, codec, &sample_rate, &output_sample_rate); | |||
| if (codec->codec_id == AV_CODEC_ID_AAC) | |||
| get_aac_sample_rates(s, codec, &sample_rate, &output_sample_rate); | |||
| track = start_ebml_master(pb, MATROSKA_ID_TRACKENTRY, 0); | |||
| put_ebml_uint (pb, MATROSKA_ID_TRACKNUMBER , i + 1); | |||
| put_ebml_uint (pb, MATROSKA_ID_TRACKUID , i + 1); | |||
| put_ebml_uint (pb, MATROSKA_ID_TRACKFLAGLACING , 0); // no lacing (yet) | |||
| track = start_ebml_master(pb, MATROSKA_ID_TRACKENTRY, 0); | |||
| put_ebml_uint (pb, MATROSKA_ID_TRACKNUMBER , i + 1); | |||
| put_ebml_uint (pb, MATROSKA_ID_TRACKUID , i + 1); | |||
| put_ebml_uint (pb, MATROSKA_ID_TRACKFLAGLACING , 0); // no lacing (yet) | |||
| if ((tag = av_dict_get(st->metadata, "title", NULL, 0))) | |||
| put_ebml_string(pb, MATROSKA_ID_TRACKNAME, tag->value); | |||
| tag = av_dict_get(st->metadata, "language", NULL, 0); | |||
| put_ebml_string(pb, MATROSKA_ID_TRACKLANGUAGE, tag ? tag->value:"und"); | |||
| if ((tag = av_dict_get(st->metadata, "title", NULL, 0))) | |||
| put_ebml_string(pb, MATROSKA_ID_TRACKNAME, tag->value); | |||
| tag = av_dict_get(st->metadata, "language", NULL, 0); | |||
| put_ebml_string(pb, MATROSKA_ID_TRACKLANGUAGE, tag ? tag->value:"und"); | |||
| // The default value for TRACKFLAGDEFAULT is 1, so add element | |||
| // if we need to clear it. | |||
| if (!(st->disposition & AV_DISPOSITION_DEFAULT)) | |||
| put_ebml_uint(pb, MATROSKA_ID_TRACKFLAGDEFAULT, !!(st->disposition & AV_DISPOSITION_DEFAULT)); | |||
| // The default value for TRACKFLAGDEFAULT is 1, so add element | |||
| // if we need to clear it. | |||
| if (!(st->disposition & AV_DISPOSITION_DEFAULT)) | |||
| put_ebml_uint(pb, MATROSKA_ID_TRACKFLAGDEFAULT, !!(st->disposition & AV_DISPOSITION_DEFAULT)); | |||
| if (codec->codec_type == AVMEDIA_TYPE_AUDIO && codec->delay) { | |||
| mkv->tracks[i].ts_offset = av_rescale_q(codec->delay, | |||
| (AVRational){ 1, codec->sample_rate }, | |||
| st->time_base); | |||
| if (codec->codec_type == AVMEDIA_TYPE_AUDIO && codec->delay) { | |||
| mkv->tracks[i].ts_offset = av_rescale_q(codec->delay, | |||
| (AVRational){ 1, codec->sample_rate }, | |||
| st->time_base); | |||
| put_ebml_uint(pb, MATROSKA_ID_CODECDELAY, | |||
| av_rescale_q(codec->delay, (AVRational){ 1, codec->sample_rate }, | |||
| (AVRational){ 1, 1000000000 })); | |||
| } | |||
| put_ebml_uint(pb, MATROSKA_ID_CODECDELAY, | |||
| av_rescale_q(codec->delay, (AVRational){ 1, codec->sample_rate }, | |||
| (AVRational){ 1, 1000000000 })); | |||
| // look for a codec ID string specific to mkv to use, | |||
| // if none are found, use AVI codes | |||
| for (j = 0; ff_mkv_codec_tags[j].id != AV_CODEC_ID_NONE; j++) { | |||
| if (ff_mkv_codec_tags[j].id == codec->codec_id) { | |||
| put_ebml_string(pb, MATROSKA_ID_CODECID, ff_mkv_codec_tags[j].str); | |||
| native_id = 1; | |||
| break; | |||
| } | |||
| } | |||
| if (mkv->mode == MODE_WEBM && !(codec->codec_id == AV_CODEC_ID_VP8 || | |||
| codec->codec_id == AV_CODEC_ID_VP9 || | |||
| codec->codec_id == AV_CODEC_ID_OPUS || | |||
| codec->codec_id == AV_CODEC_ID_VORBIS)) { | |||
| av_log(s, AV_LOG_ERROR, | |||
| "Only VP8 or VP9 video and Vorbis or Opus audio are supported for WebM.\n"); | |||
| return AVERROR(EINVAL); | |||
| } | |||
| // look for a codec ID string specific to mkv to use, | |||
| // if none are found, use AVI codes | |||
| for (j = 0; ff_mkv_codec_tags[j].id != AV_CODEC_ID_NONE; j++) { | |||
| if (ff_mkv_codec_tags[j].id == codec->codec_id) { | |||
| put_ebml_string(pb, MATROSKA_ID_CODECID, ff_mkv_codec_tags[j].str); | |||
| native_id = 1; | |||
| switch (codec->codec_type) { | |||
| case AVMEDIA_TYPE_VIDEO: | |||
| put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_VIDEO); | |||
| if (st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0) | |||
| put_ebml_uint(pb, MATROSKA_ID_TRACKDEFAULTDURATION, 1E9 / av_q2d(st->avg_frame_rate)); | |||
| if (!native_id && | |||
| ff_codec_get_tag(ff_codec_movvideo_tags, codec->codec_id) && | |||
| (!ff_codec_get_tag(ff_codec_bmp_tags, codec->codec_id) || | |||
| codec->codec_id == AV_CODEC_ID_SVQ1 || | |||
| codec->codec_id == AV_CODEC_ID_SVQ3 || | |||
| codec->codec_id == AV_CODEC_ID_CINEPAK)) | |||
| qt_id = 1; | |||
| if (qt_id) | |||
| put_ebml_string(pb, MATROSKA_ID_CODECID, "V_QUICKTIME"); | |||
| else if (!native_id) { | |||
| // if there is no mkv-specific codec ID, use VFW mode | |||
| put_ebml_string(pb, MATROSKA_ID_CODECID, "V_MS/VFW/FOURCC"); | |||
| mkv->tracks[i].write_dts = 1; | |||
| } | |||
| subinfo = start_ebml_master(pb, MATROSKA_ID_TRACKVIDEO, 0); | |||
| // XXX: interlace flag? | |||
| put_ebml_uint (pb, MATROSKA_ID_VIDEOPIXELWIDTH , codec->width); | |||
| put_ebml_uint (pb, MATROSKA_ID_VIDEOPIXELHEIGHT, codec->height); | |||
| if ((tag = av_dict_get(s->metadata, "stereo_mode", NULL, 0))) { | |||
| uint8_t stereo_fmt = atoi(tag->value); | |||
| int valid_fmt = 0; | |||
| switch (mkv->mode) { | |||
| case MODE_WEBM: | |||
| if (stereo_fmt <= MATROSKA_VIDEO_STEREOMODE_TYPE_TOP_BOTTOM || | |||
| stereo_fmt == MATROSKA_VIDEO_STEREOMODE_TYPE_RIGHT_LEFT) | |||
| valid_fmt = 1; | |||
| break; | |||
| case MODE_MATROSKAv2: | |||
| if (stereo_fmt <= MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_RL) | |||
| valid_fmt = 1; | |||
| break; | |||
| } | |||
| } | |||
| if (mkv->mode == MODE_WEBM && !(codec->codec_id == AV_CODEC_ID_VP8 || | |||
| codec->codec_id == AV_CODEC_ID_VP9 || | |||
| codec->codec_id == AV_CODEC_ID_OPUS || | |||
| codec->codec_id == AV_CODEC_ID_VORBIS)) { | |||
| av_log(s, AV_LOG_ERROR, | |||
| "Only VP8 or VP9 video and Vorbis or Opus audio are supported for WebM.\n"); | |||
| return AVERROR(EINVAL); | |||
| if (valid_fmt) | |||
| put_ebml_uint (pb, MATROSKA_ID_VIDEOSTEREOMODE, stereo_fmt); | |||
| } | |||
| if (st->sample_aspect_ratio.num) { | |||
| int d_width = codec->width*av_q2d(st->sample_aspect_ratio); | |||
| put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYWIDTH , d_width); | |||
| put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYHEIGHT, codec->height); | |||
| put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYUNIT, 3); | |||
| } | |||
| end_ebml_master(pb, subinfo); | |||
| break; | |||
| case AVMEDIA_TYPE_AUDIO: | |||
| put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_AUDIO); | |||
| if (!native_id) | |||
| // no mkv-specific ID, use ACM mode | |||
| put_ebml_string(pb, MATROSKA_ID_CODECID, "A_MS/ACM"); | |||
| subinfo = start_ebml_master(pb, MATROSKA_ID_TRACKAUDIO, 0); | |||
| put_ebml_uint (pb, MATROSKA_ID_AUDIOCHANNELS , codec->channels); | |||
| put_ebml_float (pb, MATROSKA_ID_AUDIOSAMPLINGFREQ, sample_rate); | |||
| if (output_sample_rate) | |||
| put_ebml_float(pb, MATROSKA_ID_AUDIOOUTSAMPLINGFREQ, output_sample_rate); | |||
| if (bit_depth) | |||
| put_ebml_uint(pb, MATROSKA_ID_AUDIOBITDEPTH, bit_depth); | |||
| end_ebml_master(pb, subinfo); | |||
| break; | |||
| case AVMEDIA_TYPE_SUBTITLE: | |||
| put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_SUBTITLE); | |||
| if (!native_id) { | |||
| av_log(s, AV_LOG_ERROR, "Subtitle codec %d is not supported.\n", codec->codec_id); | |||
| return AVERROR(ENOSYS); | |||
| } | |||
| break; | |||
| default: | |||
| av_log(s, AV_LOG_ERROR, "Only audio, video, and subtitles are supported for Matroska.\n"); | |||
| break; | |||
| } | |||
| ret = mkv_write_codecprivate(s, pb, codec, native_id, qt_id); | |||
| if (ret < 0) | |||
| return ret; | |||
| switch (codec->codec_type) { | |||
| case AVMEDIA_TYPE_VIDEO: | |||
| put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_VIDEO); | |||
| if (st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0) | |||
| put_ebml_uint(pb, MATROSKA_ID_TRACKDEFAULTDURATION, 1E9 / av_q2d(st->avg_frame_rate)); | |||
| if (!native_id && | |||
| ff_codec_get_tag(ff_codec_movvideo_tags, codec->codec_id) && | |||
| (!ff_codec_get_tag(ff_codec_bmp_tags, codec->codec_id) || | |||
| codec->codec_id == AV_CODEC_ID_SVQ1 || | |||
| codec->codec_id == AV_CODEC_ID_SVQ3 || | |||
| codec->codec_id == AV_CODEC_ID_CINEPAK)) | |||
| qt_id = 1; | |||
| if (qt_id) | |||
| put_ebml_string(pb, MATROSKA_ID_CODECID, "V_QUICKTIME"); | |||
| else if (!native_id) { | |||
| // if there is no mkv-specific codec ID, use VFW mode | |||
| put_ebml_string(pb, MATROSKA_ID_CODECID, "V_MS/VFW/FOURCC"); | |||
| mkv->tracks[i].write_dts = 1; | |||
| } | |||
| end_ebml_master(pb, track); | |||
| subinfo = start_ebml_master(pb, MATROSKA_ID_TRACKVIDEO, 0); | |||
| // XXX: interlace flag? | |||
| put_ebml_uint (pb, MATROSKA_ID_VIDEOPIXELWIDTH , codec->width); | |||
| put_ebml_uint (pb, MATROSKA_ID_VIDEOPIXELHEIGHT, codec->height); | |||
| if ((tag = av_dict_get(s->metadata, "stereo_mode", NULL, 0))) { | |||
| uint8_t stereo_fmt = atoi(tag->value); | |||
| int valid_fmt = 0; | |||
| switch (mkv->mode) { | |||
| case MODE_WEBM: | |||
| if (stereo_fmt <= MATROSKA_VIDEO_STEREOMODE_TYPE_TOP_BOTTOM || | |||
| stereo_fmt == MATROSKA_VIDEO_STEREOMODE_TYPE_RIGHT_LEFT) | |||
| valid_fmt = 1; | |||
| break; | |||
| case MODE_MATROSKAv2: | |||
| if (stereo_fmt <= MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_RL) | |||
| valid_fmt = 1; | |||
| break; | |||
| } | |||
| return 0; | |||
| } | |||
| if (valid_fmt) | |||
| put_ebml_uint (pb, MATROSKA_ID_VIDEOSTEREOMODE, stereo_fmt); | |||
| } | |||
| if (st->sample_aspect_ratio.num) { | |||
| int d_width = codec->width*av_q2d(st->sample_aspect_ratio); | |||
| put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYWIDTH , d_width); | |||
| put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYHEIGHT, codec->height); | |||
| put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYUNIT, 3); | |||
| } | |||
| end_ebml_master(pb, subinfo); | |||
| break; | |||
| static int mkv_write_tracks(AVFormatContext *s) | |||
| { | |||
| MatroskaMuxContext *mkv = s->priv_data; | |||
| AVIOContext *pb = s->pb; | |||
| ebml_master tracks; | |||
| int i, ret; | |||
| case AVMEDIA_TYPE_AUDIO: | |||
| put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_AUDIO); | |||
| if (!native_id) | |||
| // no mkv-specific ID, use ACM mode | |||
| put_ebml_string(pb, MATROSKA_ID_CODECID, "A_MS/ACM"); | |||
| subinfo = start_ebml_master(pb, MATROSKA_ID_TRACKAUDIO, 0); | |||
| put_ebml_uint (pb, MATROSKA_ID_AUDIOCHANNELS , codec->channels); | |||
| put_ebml_float (pb, MATROSKA_ID_AUDIOSAMPLINGFREQ, sample_rate); | |||
| if (output_sample_rate) | |||
| put_ebml_float(pb, MATROSKA_ID_AUDIOOUTSAMPLINGFREQ, output_sample_rate); | |||
| if (bit_depth) | |||
| put_ebml_uint(pb, MATROSKA_ID_AUDIOBITDEPTH, bit_depth); | |||
| end_ebml_master(pb, subinfo); | |||
| break; | |||
| ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_TRACKS, avio_tell(pb)); | |||
| if (ret < 0) | |||
| return ret; | |||
| case AVMEDIA_TYPE_SUBTITLE: | |||
| put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_SUBTITLE); | |||
| if (!native_id) { | |||
| av_log(s, AV_LOG_ERROR, "Subtitle codec %d is not supported.\n", codec->codec_id); | |||
| return AVERROR(ENOSYS); | |||
| } | |||
| break; | |||
| default: | |||
| av_log(s, AV_LOG_ERROR, "Only audio, video, and subtitles are supported for Matroska.\n"); | |||
| break; | |||
| } | |||
| ret = mkv_write_codecprivate(s, pb, codec, native_id, qt_id); | |||
| tracks = start_ebml_master(pb, MATROSKA_ID_TRACKS, 0); | |||
| for (i = 0; i < s->nb_streams; i++) { | |||
| ret = mkv_write_track(s, mkv, i, pb); | |||
| if (ret < 0) | |||
| return ret; | |||
| end_ebml_master(pb, track); | |||
| } | |||
| end_ebml_master(pb, tracks); | |||
| return 0; | |||