diff --git a/ChangeLog b/ChangeLog index e22169ed2..56d7e3499 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2006-11-07 Moritz Bunkus + + * mkvmerge: new feature: Added support for the "stereo mode" + flag for video tracks. + 2006-11-03 Moritz Bunkus * mkvmerge: bug fix: For MP4 files with certain CTTS contents diff --git a/doc/mkvmerge.1 b/doc/mkvmerge.1 index 74e92cc4d..158c46938 100644 --- a/doc/mkvmerge.1 +++ b/doc/mkvmerge.1 @@ -452,6 +452,12 @@ exclusive. .TP \fB\-\-cropping\fR <\fITID\fR:\fIleft\fR,\fItop\fR,\fIright\fR,\fIbottom\fR> Sets the pixel cropping parameters of a video track to the given values. +.TP +\fB\-\-stereo\-mode\fR <\fITID\fR:\fIn\fR|\fIkeyword\fR> +Sets the stereo mode for the video track with the track ID \fITID\fR. +The mode can either be a number \fIn\fR between 0 and 3 or one of the +keywords \fInone\fR (same as n=0), \fIright\fR (same as n=1), \fIleft\fR +(same as n=2) or \fIboth\fR (same as n=3). .LP Options that only apply to text subtitle tracks: .TP diff --git a/src/input/r_matroska.cpp b/src/input/r_matroska.cpp index b1a658799..00be6e29f 100644 --- a/src/input/r_matroska.cpp +++ b/src/input/r_matroska.cpp @@ -1045,6 +1045,7 @@ kax_reader_c::read_headers() { KaxVideoPixelCropTop *kv_pctop; KaxVideoPixelCropRight *kv_pcright; KaxVideoPixelCropBottom *kv_pcbottom; + KaxVideoStereoMode *kv_stereo_mode; kv_pwidth = FINDFIRST(ktvideo, KaxVideoPixelWidth); if (kv_pwidth != NULL) { @@ -1118,6 +1119,13 @@ kax_reader_c::read_headers() { track->v_pcbottom); } + kv_stereo_mode = FINDFIRST(ktvideo, KaxVideoStereoMode); + if (NULL != kv_stereo_mode) { + track->v_stereo_mode = (stereo_mode_e)uint64(*kv_stereo_mode); + mxverb(2, PFX "| + Stereo mode: %d\n", + (int)track->v_stereo_mode); + } + } kcodecid = FINDFIRST(ktentry, KaxCodecID); @@ -1353,6 +1361,8 @@ kax_reader_c::init_passthrough_packetizer(kax_track_t *t) { (t->v_pcright > 0) || (t->v_pcbottom > 0))) ptzr->set_video_pixel_cropping(t->v_pcleft, t->v_pctop, t->v_pcright, t->v_pcbottom); + if (STEREO_MODE_UNSPECIFIED == ptzr->ti.stereo_mode) + ptzr->set_stereo_mode(t->v_stereo_mode); if (ptzr->get_cue_creation() == CUE_STRATEGY_UNSPECIFIED) ptzr->set_cue_creation( CUE_STRATEGY_IFRAMES); @@ -1505,6 +1515,8 @@ kax_reader_c::create_packetizer(int64_t tid) { PTZR(t->ptzr)->set_video_pixel_cropping(t->v_pcleft, t->v_pctop, t->v_pcright, t->v_pcbottom); + if (STEREO_MODE_UNSPECIFIED == PTZR(t->ptzr)->ti.stereo_mode) + PTZR(t->ptzr)->set_stereo_mode(t->v_stereo_mode); } else init_passthrough_packetizer(t); diff --git a/src/input/r_matroska.h b/src/input/r_matroska.h index 372fec142..191c259fd 100644 --- a/src/input/r_matroska.h +++ b/src/input/r_matroska.h @@ -55,6 +55,7 @@ struct kax_track_t { // Parameters for video tracks uint64_t v_width, v_height, v_dwidth, v_dheight; uint64_t v_pcleft, v_pctop, v_pcright, v_pcbottom; + stereo_mode_e v_stereo_mode; float v_frate; char v_fourcc[5]; bool v_bframes; @@ -97,6 +98,7 @@ struct kax_track_t { lacing_flag(false), v_width(0), v_height(0), v_dwidth(0), v_dheight(0), v_pcleft(0), v_pctop(0), v_pcright(0), v_pcbottom(0), + v_stereo_mode(STEREO_MODE_UNSPECIFIED), v_frate(0.0), v_bframes(false), a_channels(0), a_bps(0), a_formattag(0), diff --git a/src/merge/mkvmerge.cpp b/src/merge/mkvmerge.cpp index 4a5edc53c..d867feec3 100644 --- a/src/merge/mkvmerge.cpp +++ b/src/merge/mkvmerge.cpp @@ -231,6 +231,10 @@ set_usage() { " Explicitely set the display dimensions.\n" " --cropping \n" " Sets the cropping parameters.\n" + " --stereo-mode \n" + " Sets the stereo mode parameter. It can\n" + " either be a numer 0 - 3 or one of the\n" + " keywords 'none', 'right', 'left' or 'both'.\n" "\n Options that only apply to text subtitle tracks:\n" " --sub-charset \n" " Sets the charset the text subtitles are\n" @@ -668,6 +672,45 @@ parse_cropping(const string &s, ti.pixel_crop_list[id] = crop; } +/** \brief Parse the \c --stereo-mode argument + + The argument must either be a number between 0 and 3 or + one of the keywords \c 'none', \c 'left', \c 'right' or \c 'both'. +*/ +static void +parse_stereo_mode(const string &s, + track_info_c &ti) { + static const char * const keywords[] = { + "none", "right", "left", "both", NULL + }; + static const char *errmsg = + _("Stereo mode parameter: not given in the form " + ": where n is a number between 0 and 3 " + "or one of the keywords 'none', 'right', 'left', 'both' " + "(argument was '%s').\n"); + int64_t i, id; + vector v; + + v = split(s, ":"); + if (v.size() != 2) + mxerror(errmsg, s.c_str()); + + id = 0; + if (!parse_int(v[0], id)) + mxerror(errmsg, s.c_str()); + + for (i = 0; NULL != keywords[i]; ++i) + if (v[1] == keywords[i]) { + ti.stereo_mode_list[id] = (stereo_mode_e)i; + return; + } + + if (!parse_int(v[1], i) || (i < 0) || (i > STEREO_MODE_BOTH)) + mxerror(errmsg, s.c_str()); + + ti.stereo_mode_list[id] = (stereo_mode_e)i; +} + /** \brief Parse the duration formats to \c --split This function is called by ::parse_split if the format specifies @@ -1806,6 +1849,13 @@ parse_args(vector args) { parse_cropping(next_arg, *ti); sit++; + } else if (this_arg == "--stereo-mode") { + if (no_next_arg) + mxerror(_("'--stereo-mode' lacks the crop parameters.\n")); + + parse_stereo_mode(next_arg, *ti); + sit++; + } else if ((this_arg == "-y") || (this_arg == "--sync")) { if (no_next_arg) mxerror(_("'%s' lacks the audio delay.\n"), this_arg.c_str()); diff --git a/src/merge/pr_generic.cpp b/src/merge/pr_generic.cpp index 18f50b20c..3267f77cd 100644 --- a/src/merge/pr_generic.cpp +++ b/src/merge/pr_generic.cpp @@ -182,6 +182,12 @@ generic_packetizer_c::generic_packetizer_c(generic_reader_c *nreader, else ti.pixel_cropping_specified = false; + // Let's see if the user has specified a stereo mode for this track. + if (map_has_key(ti.stereo_mode_list, ti.id)) + ti.stereo_mode = ti.stereo_mode_list[ti.id]; + else if (map_has_key(ti.stereo_mode_list, -1)) + ti.stereo_mode = ti.stereo_mode_list[-1]; + // Let's see if the user has specified a default duration for this track. htrack_default_duration = -1; default_duration_forced = true; @@ -527,6 +533,19 @@ generic_packetizer_c::set_video_pixel_cropping(int left, } } +void +generic_packetizer_c::set_stereo_mode(stereo_mode_e stereo_mode) { + ti.stereo_mode = stereo_mode; + + if ((NULL != track_entry) && + (STEREO_MODE_UNSPECIFIED != stereo_mode)) { + KaxTrackVideo &video = GetChild(*track_entry); + + *(static_cast + (&GetChild(video))) = ti.stereo_mode; + } +} + void generic_packetizer_c::set_headers() { int idx, disp_width, disp_height; @@ -689,6 +708,10 @@ generic_packetizer_c::set_headers() { (&GetChild(video))) = ti.pixel_cropping.bottom; } + + if (STEREO_MODE_UNSPECIFIED != ti.stereo_mode) + *(static_cast + (&GetChild(video))) = ti.stereo_mode; } } else if (htrack_type == track_audio) { @@ -1393,6 +1416,7 @@ track_info_c::track_info_c(): packet_delay(0), compression(COMPRESSION_UNSPECIFIED), pixel_cropping_specified(false), + stereo_mode(STEREO_MODE_UNSPECIFIED), no_chapters(false), no_attachments(false), no_tags(false), @@ -1482,6 +1506,9 @@ track_info_c::operator =(const track_info_c &src) { pixel_cropping = src.pixel_cropping; pixel_cropping_specified = src.pixel_cropping_specified; + stereo_mode_list = src.stereo_mode_list; + stereo_mode = src.stereo_mode; + no_chapters = src.no_chapters; no_attachments = src.no_attachments; no_tags = src.no_tags; diff --git a/src/merge/pr_generic.h b/src/merge/pr_generic.h index 3846ee66b..c4e6c9b6d 100644 --- a/src/merge/pr_generic.h +++ b/src/merge/pr_generic.h @@ -103,6 +103,14 @@ struct pixel_crop_t { pixel_crop_t(): left(0), top(0), right(0), bottom(0) {} }; +enum stereo_mode_e { + STEREO_MODE_UNSPECIFIED = -1, + STEREO_MODE_NONE = 0, + STEREO_MODE_RIGHT = 1, + STEREO_MODE_LEFT = 2, + STEREO_MODE_BOTH = 3, +}; + class track_info_c { protected: bool initialized; @@ -163,6 +171,9 @@ public: pixel_crop_t pixel_cropping; // For this very track bool pixel_cropping_specified; + map stereo_mode_list; // As given on the command line + stereo_mode_e stereo_mode; // For this very track + map default_durations; // As given on the command line map max_blockadd_ids; // As given on the command line @@ -448,6 +459,7 @@ public: } virtual void set_video_pixel_cropping(int left, int top, int right, int bottom); + virtual void set_stereo_mode(stereo_mode_e stereo_mode); virtual void set_as_default_track(int type, int priority); diff --git a/tests/results.txt b/tests/results.txt index f4caca30f..3f699e762 100644 --- a/tests/results.txt +++ b/tests/results.txt @@ -68,3 +68,4 @@ T_218theora:35ea3cf1add9a6687627953e1645d4c9-fac4b041366588e3c8215b9fbb696db2:pa T_219srt_short_timecodes:a7db07ee64751fcc24993dd4af73ccf1:passed:20060926-112658 T_220ass_with_comments_at_start:51ccd0cbe4e60b21817d7547310250bb:passed:20060926-120101 T_221aac_lc_misdetected_as_sbr:f980b6252a24a5f855ec90e3d9d3a1fe:passed:20061103-174221 +T_222stereo_mode:22eebd6bda7d848379044be8d9d8f440-38a1988c087c40e6e52025bb974f2c56-38a1988c087c40e6e52025bb974f2c56:passed:20061107-092251 diff --git a/tests/test-222stereo_mode.rb b/tests/test-222stereo_mode.rb new file mode 100644 index 000000000..da173d76b --- /dev/null +++ b/tests/test-222stereo_mode.rb @@ -0,0 +1,42 @@ +#!/usr/bin/ruby -w + +class T_222stereo_mode < Test + def description + return "mkvmerge / Stereo mode flag for video tracks / in(AVI)" + end + + def run + merge(tmp + "-1", + "-A --stereo-mode 0:none data/avi/v.avi " + + "-A --stereo-mode 0:right data/avi/v.avi " + + "-A --stereo-mode 0:left data/avi/v.avi " + + "-A --stereo-mode 0:both data/avi/v.avi ") + if (!FileTest.exist?(tmp + "-1")) + error("First merge failed") + end + + hash = hash_file(tmp + "-1") + + merge(tmp + "-2", + "--stereo-mode 4:none --stereo-mode 3:right " + + "--stereo-mode 2:left --stereo-mode 1:both #{tmp}-1") + File.unlink(tmp + "-1") + if (!FileTest.exist?(tmp + "-2")) + error("Second merge failed") + end + + hash += "-" + hash_file(tmp + "-2") + + merge(tmp + "-3", tmp + "-2") + File.unlink(tmp + "-2") + if (!FileTest.exist?(tmp + "-3")) + error("Third merge failed") + end + + hash += "-" + hash_file(tmp + "-3") + File.unlink(tmp + "-3") + + return hash + end +end +