Note that since webm has no official subtitle support, only burned in subtitles can be used with this muxer at this time.
void
ghb_update_destination_extension(signal_user_data_t *ud)
{
- static gchar *containers[] = {".mkv", ".mp4", ".m4v", ".error", NULL};
+ static gchar *containers[] = {".mkv", ".mp4", ".m4v", ".webm", ".error", NULL};
gchar *filename;
const gchar *extension;
gint ii;
<object class="GtkFileFilter" id="SourceFilterEVO"/>
<object class="GtkFileFilter" id="SourceFilterFLV"/>
<object class="GtkFileFilter" id="SourceFilterMKV"/>
+ <object class="GtkFileFilter" id="SourceFilterWebM"/>
<object class="GtkFileFilter" id="SourceFilterMOV"/>
<object class="GtkFileFilter" id="SourceFilterMP4"/>
<object class="GtkFileFilter" id="SourceFilterMPG"/>
mux_id = ghb_dict_get_string(settings, "FileFormat");
mux = ghb_lookup_container_by_name(mux_id);
+ gboolean v_unsup = FALSE;
+
vcodec = ghb_settings_video_encoder_codec(settings, "VideoEncoder");
if ((mux->format & HB_MUX_MASK_MP4) && (vcodec == HB_VCODEC_THEORA))
{
_("Theora is not supported in the MP4 container.\n\n"
"You should choose a different video codec or container.\n"
"If you continue, FFMPEG will be chosen for you."));
+ v_unsup = TRUE;
+ }
+ else if ((mux->format & HB_MUX_MASK_WEBM) &&
+ (vcodec != HB_VCODEC_FFMPEG_VP8 && vcodec != HB_VCODEC_FFMPEG_VP9))
+ {
+ // webm only supports vp8 and vp9.
+ message = g_strdup_printf(
+ _("Only VP8 or VP9 is supported in the WebM container.\n\n"
+ "You should choose a different video codec or container.\n"
+ "If you continue, one will be chosen for you."));
+ v_unsup = TRUE;
+ }
+
+ if (v_unsup)
+ {
if (!ghb_message_dialog(parent, GTK_MESSAGE_WARNING,
message, _("Cancel"), _("Continue")))
{
ghb_dict_set_string(settings, "VideoEncoder",
hb_video_encoder_get_short_name(vcodec));
}
+
return TRUE;
}
gint count, ii, track;
gboolean burned, one_burned = FALSE;
+ const char *mux_id;
+ const hb_container_t *mux;
+
+ mux_id = ghb_dict_get_string(settings, "FileFormat");
+ mux = ghb_lookup_container_by_name(mux_id);
+
slist = ghb_get_job_subtitle_list(settings);
count = ghb_array_len(slist);
for (ii = 0; ii < count; ii++)
{
one_burned = TRUE;
}
+ else if (mux->format & HB_MUX_MASK_WEBM)
+ {
+ // WebM can only handle burned subs afaik. Their specs are ambiguous here
+ message = g_strdup_printf(
+ _("WebM in HandBrake only supports burned subtitles.\n\n"
+ "You should change your subtitle selections.\n"
+ "If you continue, some subtitles will be lost."));
+ if (!ghb_message_dialog(parent, GTK_MESSAGE_WARNING,
+ message, _("Cancel"), _("Continue")))
+ {
+ g_free(message);
+ return FALSE;
+ }
+ g_free(message);
+ break;
+ }
if (import != NULL)
{
const gchar *filename;
{
codec = HB_ACODEC_LAME;
}
+ else if (mux->format & HB_MUX_MASK_WEBM)
+ {
+ codec = hb_audio_encoder_get_default(mux->format);
+ }
else
{
codec = HB_ACODEC_FFAAC;
const char *name = hb_audio_encoder_get_short_name(codec);
ghb_dict_set_string(asettings, "Encoder", name);
}
- gchar *a_unsup = NULL;
- gchar *mux_s = NULL;
+ const gchar *a_unsup = NULL;
+ const gchar *mux_s = NULL;
if (mux->format & HB_MUX_MASK_MP4)
{
mux_s = "MP4";
codec = HB_ACODEC_FFAAC;
}
}
+ if (mux->format & HB_MUX_MASK_WEBM)
+ {
+ mux_s = "WebM";
+ // WebM only supports Vorbis and Opus codecs
+ if (codec != HB_ACODEC_VORBIS && codec != HB_ACODEC_OPUS)
+ {
+ a_unsup = hb_audio_encoder_get_short_name(codec);
+ codec = hb_audio_encoder_get_default(mux->format);
+ }
+ }
if (a_unsup)
{
message = g_strdup_printf(
SourceFilterEVO
SourceFilterVOB
SourceFilterMKV
+ SourceFilterWebM
SourceFilterMP4
SourceFilterAVI
SourceFilterMOV
gtk_file_filter_add_pattern(filter, "*.mkv");
gtk_file_filter_add_pattern(filter, "*.MKV");
gtk_file_chooser_add_filter(chooser, filter);
+ filter = GTK_FILE_FILTER(GHB_OBJECT(ud->builder, "SourceFilterWebM"));
+ gtk_file_filter_set_name(filter, "WebM");
+ gtk_file_filter_add_pattern(filter, "*.webm");
+ gtk_file_chooser_add_filter(chooser, filter);
filter = GTK_FILE_FILTER(GHB_OBJECT(ud->builder, "SourceFilterMP4"));
gtk_file_filter_set_name(filter, "MP4");
gtk_file_filter_add_pattern(filter, "*.mp4");
HB_GID_ACODEC_OPUS,
HB_GID_MUX_MKV,
HB_GID_MUX_MP4,
+ HB_GID_MUX_WEBM,
};
#define HB_VIDEO_CLOCK 27000000 // 27MHz clock
{ { "H.265 (VideoToolbox)","vt_h265", "H.265 (libavcodec)", HB_VCODEC_FFMPEG_VT_H265, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H265, },
{ { "MPEG-4", "mpeg4", "MPEG-4 (libavcodec)", HB_VCODEC_FFMPEG_MPEG4, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_MPEG4, },
{ { "MPEG-2", "mpeg2", "MPEG-2 (libavcodec)", HB_VCODEC_FFMPEG_MPEG2, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_MPEG2, },
- { { "VP8", "VP8", "VP8 (libvpx)", HB_VCODEC_FFMPEG_VP8, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_VP8, },
- { { "VP9", "VP9", "VP9 (libvpx)", HB_VCODEC_FFMPEG_VP9, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_VP9, },
+ { { "VP8", "VP8", "VP8 (libvpx)", HB_VCODEC_FFMPEG_VP8, HB_MUX_MASK_WEBM|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_VP8, },
+ { { "VP9", "VP9", "VP9 (libvpx)", HB_VCODEC_FFMPEG_VP9, HB_MUX_MASK_WEBM|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_VP9, },
{ { "Theora", "theora", "Theora (libtheora)", HB_VCODEC_THEORA, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_THEORA, },
};
int hb_video_encoders_count = sizeof(hb_video_encoders) / sizeof(hb_video_encoders[0]);
{ { "DTS-HD Passthru", "copy:dtshd", "DTS-HD Passthru", HB_ACODEC_DCA_HD_PASS, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_DTSHD_PASS, },
{ { "MP3", "mp3", "MP3 (libmp3lame)", HB_ACODEC_LAME, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_MP3, },
{ { "MP3 Passthru", "copy:mp3", "MP3 Passthru", HB_ACODEC_MP3_PASS, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_MP3_PASS, },
- { { "Vorbis", "vorbis", "Vorbis (libvorbis)", HB_ACODEC_VORBIS, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_VORBIS, },
+ { { "Vorbis", "vorbis", "Vorbis (libvorbis)", HB_ACODEC_VORBIS, HB_MUX_MASK_WEBM|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_VORBIS, },
{ { "FLAC 16-bit", "flac16", "FLAC 16-bit (libavcodec)", HB_ACODEC_FFFLAC, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_FLAC, },
{ { "FLAC 24-bit", "flac24", "FLAC 24-bit (libavcodec)", HB_ACODEC_FFFLAC24, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_FLAC, },
{ { "FLAC Passthru", "copy:flac", "FLAC Passthru", HB_ACODEC_FLAC_PASS, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_FLAC_PASS, },
- { { "Opus", "opus", "Opus (libopus)", HB_ACODEC_OPUS, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_OPUS, },
+ { { "Opus", "opus", "Opus (libopus)", HB_ACODEC_OPUS, HB_MUX_MASK_WEBM|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_OPUS, },
{ { "Auto Passthru", "copy", "Auto Passthru", HB_ACODEC_AUTO_PASS, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_AUTO_PASS, },
};
int hb_audio_encoders_count = sizeof(hb_audio_encoders) / sizeof(hb_audio_encoders[0]);
hb_container_internal_t hb_containers[] =
{
// legacy muxers, back to HB 0.9.4 whenever possible (disabled)
- { { "M4V file", "m4v", NULL, "m4v", 0, }, NULL, 0, HB_GID_MUX_MP4, },
- { { "MP4 file", "mp4", NULL, "mp4", 0, }, NULL, 0, HB_GID_MUX_MP4, },
- { { "MKV file", "mkv", NULL, "mkv", 0, }, NULL, 0, HB_GID_MUX_MKV, },
+ { { "M4V file", "m4v", NULL, "m4v", 0, }, NULL, 0, HB_GID_MUX_MP4, },
+ { { "MP4 file", "mp4", NULL, "mp4", 0, }, NULL, 0, HB_GID_MUX_MP4, },
+ { { "MKV file", "mkv", NULL, "mkv", 0, }, NULL, 0, HB_GID_MUX_MKV, },
// actual muxers
- { { "MPEG-4 (avformat)", "av_mp4", "MPEG-4 (libavformat)", "mp4", HB_MUX_AV_MP4, }, NULL, 1, HB_GID_MUX_MP4, },
- { { "MPEG-4 (mp4v2)", "mp4v2", "MPEG-4 (libmp4v2)", "mp4", HB_MUX_MP4V2, }, NULL, 1, HB_GID_MUX_MP4, },
- { { "Matroska (avformat)", "av_mkv", "Matroska (libavformat)", "mkv", HB_MUX_AV_MKV, }, NULL, 1, HB_GID_MUX_MKV, },
- { { "Matroska (libmkv)", "libmkv", "Matroska (libmkv)", "mkv", HB_MUX_LIBMKV, }, NULL, 1, HB_GID_MUX_MKV, },
+ { { "MPEG-4 (avformat)", "av_mp4", "MPEG-4 (libavformat)", "mp4", HB_MUX_AV_MP4, }, NULL, 1, HB_GID_MUX_MP4, },
+ { { "MPEG-4 (mp4v2)", "mp4v2", "MPEG-4 (libmp4v2)", "mp4", HB_MUX_MP4V2, }, NULL, 1, HB_GID_MUX_MP4, },
+ { { "Matroska (avformat)", "av_mkv", "Matroska (libavformat)", "mkv", HB_MUX_AV_MKV, }, NULL, 1, HB_GID_MUX_MKV, },
+ { { "Matroska (libmkv)", "libmkv", "Matroska (libmkv)", "mkv", HB_MUX_LIBMKV, }, NULL, 1, HB_GID_MUX_MKV, },
+ { { "WebM (avformat)", "av_webm", "WebM (libavformat)", "webm", HB_MUX_AV_WEBM, }, NULL, 1, HB_GID_MUX_WEBM, },
};
int hb_containers_count = sizeof(hb_containers) / sizeof(hb_containers[0]);
static int hb_container_is_enabled(int format)
{
case HB_MUX_AV_MP4:
case HB_MUX_AV_MKV:
+ case HB_MUX_AV_WEBM:
return 1;
default:
return 0;
} break;
+ // webm can't support subtitles unless they're burned.
+ // there's ambiguity in the spec about WebVTT... TODO
+ case HB_MUX_AV_WEBM:
+ return 0;
default:
// Internal error. Should never get here.
hb_error("internal error. Bad mux %d\n", mux);
* mux: output file format
* file: file path
*/
-#define HB_MUX_MASK 0xFF0001
-#define HB_MUX_INVALID 0x000000
-#define HB_MUX_MP4V2 0x010000
-#define HB_MUX_AV_MP4 0x020000
-#define HB_MUX_MASK_MP4 0x030000
-#define HB_MUX_LIBMKV 0x100000
-#define HB_MUX_AV_MKV 0x200000
-#define HB_MUX_MASK_MKV 0x300000
-#define HB_MUX_MASK_AV 0x220000
+#define HB_MUX_MASK 0xFF0001
+#define HB_MUX_INVALID 0x000000
+#define HB_MUX_MP4V2 0x010000
+#define HB_MUX_AV_MP4 0x020000
+#define HB_MUX_MASK_MP4 0x030000
+#define HB_MUX_LIBMKV 0x100000
+#define HB_MUX_AV_MKV 0x200000
+#define HB_MUX_AV_WEBM 0x400000
+#define HB_MUX_MASK_MKV 0x300000
+#define HB_MUX_MASK_AV 0x620000
+#define HB_MUX_MASK_WEBM 0x400000
+
/* default muxer for each container */
-#define HB_MUX_MP4 HB_MUX_AV_MP4
-#define HB_MUX_MKV HB_MUX_AV_MKV
+#define HB_MUX_MP4 HB_MUX_AV_MP4
+#define HB_MUX_MKV HB_MUX_AV_MKV
+#define HB_MUX_WEBM HB_MUX_AV_WEBM
int mux;
char * file;
{
// Parse ADTS AAC streams for AudioSpecificConfig.
// This data is required in order to write
- // proper headers in MP4 and MKV files.
+ // proper headers in MP4, WebM, and MKV files.
parse_adts_extradata(audio, context, &avp);
}
DECLARE_MUX( mp4 );
DECLARE_MUX( mkv );
+DECLARE_MUX( webm );
DECLARE_MUX( avformat );
void hb_deinterlace(hb_buffer_t *dst, hb_buffer_t *src);
{
META_MUX_MP4,
META_MUX_MKV,
+ META_MUX_WEBM,
META_MUX_LAST
};
out = iso639_2;
break;
case HB_MUX_AV_MKV:
+ case HB_MUX_AV_WEBM: // webm is a subset of mkv
// MKV lang codes should be ISO-639-2B if it exists,
// else ISO-639-2
lang = lang_for_code2( iso639_2 );
meta_mux = META_MUX_MKV;
break;
+ case HB_MUX_AV_WEBM:
+ // libavformat is essentially hard coded such that it only
+ // works with a timebase of 1/1000
+ m->time_base.num = 1;
+ m->time_base.den = 1000;
+ muxer_name = "webm";
+ meta_mux = META_MUX_WEBM;
+ break;
+
default:
{
hb_error("Invalid Mux %x", job->mux);
return 0;
}
- if (track->type == MUX_TYPE_VIDEO && (job->mux & HB_MUX_MASK_MKV) &&
+ if (track->type == MUX_TYPE_VIDEO &&
+ (job->mux & (HB_MUX_MASK_MKV | HB_MUX_MASK_WEBM)) &&
buf->s.renderOffset < 0)
{
// libav matroska muxer doesn't write dts to the output, but
{
case HB_MUX_AV_MP4:
case HB_MUX_AV_MKV:
+ case HB_MUX_AV_WEBM:
mux->m = hb_mux_avformat_init( job );
break;
default:
}
}
- return HB_ACODEC_CA_AAC;
+ return hb_audio_encoder_get_default(self.container);
}
else
{
{
title = HBKitLocalizedString(@"MKV File", @"HBJob -> Format display name");
}
+ else if (container->format & HB_MUX_MASK_WEBM)
+ {
+ title = HBKitLocalizedString(@"WebM File", @"HBJob -> Format display name");
+ }
else
{
title = @(container->name);
{
return HBKitLocalizedString(@"MKV File", @"HBJob -> Format display name");
}
+ else if (container & HB_MUX_MASK_WEBM)
+ {
+ return HBKitLocalizedString(@"WebM File", @"HBJob -> Format display name");
+ }
else
{
const char *name = hb_container_get_name(container);
{
return @(HB_MUX_AV_MKV);
}
+ else if ([value isEqualToString:HBKitLocalizedString(@"WebM File", @"HBJob -> Format display name")])
+ {
+ return @(HB_MUX_AV_WEBM);
+ }
return @(hb_container_get_from_name([value UTF8String]));
}
{
destURL = [HBPreviewGenerator generateFileURLForType:@"mkv"];
}
+ else if (self.job.container & 0x400000 /*HB_MUX_MASK_WEBM*/)
+ {
+ destURL = [HBPreviewGenerator generateFileURLForType:@"webm"];
+ }
// return if we couldn't get the fileURL.
if (!destURL)
if (!encoderSupported)
{
- self.encoder = HB_VCODEC_X264;
+ self.encoder = hb_video_encoder_get_default(self.job.container);
}
}
/* HBJob -> Format display name */
"MKV File" = "MKV-Datei";
+/* HBJob -> Format display name */
+"WebM File" = "WebM-Datei";
+
/* HBJob -> Format display name */
"MP4 File" = "MP4-Datei";
[DisplayName("MP4")]
MP4,
[DisplayName("MKV")]
- MKV
+ MKV,
+ [DisplayName("WebM")]
+ WebM
}
}
encoders.Remove(AudioEncoder.fdkheaac);\r
}\r
\r
- if (task != null && task.OutputFormat != OutputFormat.Mkv)\r
+ if (task != null && task.OutputFormat == OutputFormat.Mp4)\r
{\r
encoders.Remove(AudioEncoder.Vorbis);\r
encoders.Remove(AudioEncoder.ffflac);\r
\r
encoders.Remove(AudioEncoder.TrueHDPassthrough);\r
}\r
+ else if (task != null && task.OutputFormat == OutputFormat.WebM)\r
+ {\r
+ encoders.RemoveAll(ae => !(ae.Equals(AudioEncoder.Vorbis) || ae.Equals(AudioEncoder.Opus)));\r
+ }\r
\r
// Hide the Passthru options and show the "None" option\r
if (parameter != null && parameter.ToString() == "True")\r
encoders.Remove(VideoEncoder.X265_12);\r
}\r
\r
- if (task != null && task.OutputFormat != OutputFormat.Mkv)\r
+ if (task != null && task.OutputFormat == OutputFormat.Mp4)\r
{\r
encoders.Remove(VideoEncoder.Theora);\r
encoders.Remove(VideoEncoder.VP8);\r
encoders.Remove(VideoEncoder.VP9);\r
}\r
+ else if (task != null && task.OutputFormat == OutputFormat.WebM)\r
+ {\r
+ encoders.RemoveAll(ve => !(ve.Equals(VideoEncoder.VP9) || ve.Equals(VideoEncoder.VP8)));\r
+ }\r
\r
if (!isQsvEnabled || !SystemInfo.IsQsvAvailableH264)\r
{\r
{\r
destinationFilename += ".mkv";\r
}\r
+ else if (task.OutputFormat == OutputFormat.WebM)\r
+ {\r
+ destinationFilename += ".webm";\r
+ }\r
\r
/*\r
* File Destination Path\r
}
}
+ /// <summary>
+ /// Looks up a localized string similar to WebM in HandBrake only supports burned subtitles.
+ ///
+ ///You should change your subtitle selections.
+ ///
+ ///If you continue, your non-burned subtitles will be lost..
+ /// </summary>
+ public static string Subtitles_WebmSubtitleIncompatibilityError {
+ get {
+ return ResourceManager.GetString("Subtitles_WebmSubtitleIncompatibilityError", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to WebM Subtitle Compatibility.
+ /// </summary>
+ public static string Subtitles_WebmSubtitleIncompatibilityHeader {
+ get {
+ return ResourceManager.GetString("Subtitles_WebmSubtitleIncompatibilityHeader", resourceCulture);
+ }
+ }
+
/// <summary>
/// Looks up a localized string similar to Add Closed Captions when available.
/// </summary>
Foreign Audio Track - The Foreign Audio track will be burned in if available. \r
First Track - The first track will be burned in.\r
Foreign Audio Preferred, else First - If the foreign audio track exists, it will be burned in, otherwise the first track will be chosen.</value>\r
+ </data>\r
+ <data name="Subtitles_WebmSubtitleIncompatibilityHeader" xml:space="preserve">\r
+ <value>WebM Subtitle Compatibility</value>\r
+ </data>\r
+ <data name="Subtitles_WebmSubtitleIncompatibilityError" xml:space="preserve">\r
+ <value>WebM in HandBrake only supports burned subtitles.\r
+\r
+You should change your subtitle selections.\r
+\r
+If you continue, your non-burned subtitles will be lost.</value>\r
</data>\r
<data name="Main_ScanNoTitlesFound" xml:space="preserve">\r
<value>No valid source or titles found.</value>\r
[DisplayName("MKV")]
[ShortName("mkv")]
Mkv,
+
+ [DisplayName("WebM")]
+ [ShortName("webm")]
+ WebM
}
}
}\r
}\r
\r
+ if (this.Task.OutputFormat == OutputFormat.WebM)\r
+ {\r
+ foreach (AudioTrack track in this.Task.AudioTracks.Where(track => track.Encoder != AudioEncoder.Vorbis && track.Encoder != AudioEncoder.Opus))\r
+ {\r
+ track.Encoder = AudioEncoder.Vorbis;\r
+ }\r
+ }\r
+\r
this.AudioDefaultsViewModel.RefreshTask();\r
}\r
\r
/// String array of files.\r
/// </param>\r
void Import(string[] subtitleFiles);\r
+\r
+ /// <summary>\r
+ /// Trigger a Notify Property Changed on the Task to force various UI elements to update.\r
+ /// </summary>\r
+ void RefreshTask();\r
+\r
+ /// <summary>\r
+ /// Checks the configuration of the subtitles and warns the user about any potential issues.\r
+ /// </summary>\r
+ bool ValidateSubtitles();\r
}\r
}\r
/// <summary>\r
/// Gets RangeMode.\r
/// </summary>\r
- public IEnumerable<OutputFormat> OutputFormats => new List<OutputFormat> { OutputFormat.Mp4, OutputFormat.Mkv };\r
+ public IEnumerable<OutputFormat> OutputFormats => new List<OutputFormat> { OutputFormat.Mp4, OutputFormat.Mkv, OutputFormat.WebM };\r
\r
/// <summary>\r
/// Gets or sets Destination.\r
case ".m4v":\r
this.SummaryViewModel.SetContainer(OutputFormat.Mp4);\r
break;\r
+ case ".webm":\r
+ this.SummaryViewModel.SetContainer(OutputFormat.WebM);\r
+ break;\r
}\r
}\r
else\r
\r
this.VideoViewModel.RefreshTask();\r
this.AudioViewModel.RefreshTask();\r
+ this.SubtitleViewModel.RefreshTask();\r
}\r
\r
public void Shutdown()\r
return new AddQueueError(Resources.Main_MatchingFileOverwriteWarning, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error);\r
}\r
\r
+ // defer to subtitle's validation messages\r
+ if (!this.SubtitleViewModel.ValidateSubtitles())\r
+ {\r
+ return false;\r
+ }\r
+\r
QueueTask task = new QueueTask(new EncodeTask(this.CurrentTask), HBConfigurationFactory.Create(), this.ScannedSource.ScanPath, this.SelectedPreset);\r
\r
if (!this.queueProcessor.CheckForDestinationPathDuplicates(task.Task.Destination))\r
{\r
SaveFileDialog saveFileDialog = new SaveFileDialog\r
{\r
- Filter = "mp4|*.mp4;*.m4v|mkv|*.mkv", \r
+ Filter = "mp4|*.mp4;*.m4v|mkv|*.mkv|webm|*.webm", \r
CheckPathExists = true, \r
AddExtension = true, \r
DefaultExt = ".mp4", \r
saveFileDialog.FilterIndex = !string.IsNullOrEmpty(this.CurrentTask.Destination)\r
&& !string.IsNullOrEmpty(extension)\r
? (extension == ".mp4" || extension == ".m4v" ? 1 : 2)\r
- : (this.CurrentTask.OutputFormat == OutputFormat.Mkv ? 2 : 0);\r
+ : (this.CurrentTask.OutputFormat == OutputFormat.Mkv \r
+ ? 2 \r
+ : (this.CurrentTask.OutputFormat == OutputFormat.WebM ? 3 : 0));\r
\r
string mruDir = this.GetMru(Constants.FileSaveMru);\r
if (!string.IsNullOrEmpty(mruDir))\r
case ".m4v":\r
this.SummaryViewModel.SetContainer(OutputFormat.Mp4);\r
break;\r
+ case ".webm":\r
+ this.SummaryViewModel.SetContainer(OutputFormat.WebM);\r
+ break;\r
}\r
\r
this.NotifyOfPropertyChange(() => this.CurrentTask);\r
// Filename handling.\r
if (string.IsNullOrEmpty(encodeTask.Destination))\r
{\r
- string filename = Path.ChangeExtension(Path.GetTempFileName(), encodeTask.OutputFormat == OutputFormat.Mkv ? "m4v" : "mkv");\r
+ string formatExtension;\r
+ switch (encodeTask.OutputFormat)\r
+ {\r
+ case OutputFormat.WebM:\r
+ formatExtension = "webm";\r
+ break;\r
+ case OutputFormat.Mp4:\r
+ formatExtension = "m4v";\r
+ break;\r
+ case OutputFormat.Mkv:\r
+ default:\r
+ formatExtension = "mkv";\r
+ break;\r
+ }\r
+ string filename = Path.ChangeExtension(Path.GetTempFileName(), formatExtension);\r
encodeTask.Destination = filename;\r
this.CurrentlyPlaying = filename;\r
}\r
using System.IO;\r
using System.Linq;\r
using System.Runtime.CompilerServices;\r
+ using System.Windows;\r
\r
using Caliburn.Micro;\r
\r
using HandBrakeWPF.EventArgs;\r
using HandBrakeWPF.Model.Subtitles;\r
using HandBrakeWPF.Properties;\r
+ using HandBrakeWPF.Services.Interfaces;\r
using HandBrakeWPF.Services.Presets.Model;\r
using HandBrakeWPF.Services.Scan.Model;\r
using HandBrakeWPF.ViewModels.Interfaces;\r
/// </summary>\r
public class SubtitlesViewModel : ViewModelBase, ISubtitlesViewModel\r
{\r
+ private readonly IErrorService errorService;\r
private readonly IWindowManager windowManager;\r
\r
#region Constants and Fields\r
/// <summary>\r
/// Initializes a new instance of the <see cref="HandBrakeWPF.ViewModels.SubtitlesViewModel"/> class.\r
/// </summary>\r
+ /// <param name="errorService">\r
+ /// The Error Service\r
+ /// </param>\r
/// <param name="windowManager">\r
/// The window Manager.\r
/// </param>\r
- public SubtitlesViewModel(IWindowManager windowManager)\r
+ public SubtitlesViewModel(IErrorService errorService, IWindowManager windowManager)\r
{\r
+ this.errorService = errorService;\r
this.windowManager = windowManager;\r
this.SubtitleDefaultsViewModel = new SubtitlesDefaultsViewModel();\r
this.Task = new EncodeTask();\r
}\r
}\r
\r
+ public bool IsBurnableOnly\r
+ {\r
+ get\r
+ {\r
+ return this.Task.OutputFormat == OutputFormat.WebM;\r
+ }\r
+ }\r
+\r
#endregion\r
\r
#region Public Methods\r
this.AutomaticSubtitleSelection();\r
}\r
\r
+ /// <summary>\r
+ /// Trigger a Notify Property Changed on the Task to force various UI elements to update.\r
+ /// </summary>\r
+ public void RefreshTask()\r
+ {\r
+ this.NotifyOfPropertyChange(() => this.Task);\r
+ this.NotifyOfPropertyChange(() => this.IsBurnableOnly);\r
+\r
+ if (this.IsBurnableOnly)\r
+ {\r
+ foreach (var subtitleTrack in this.Task.SubtitleTracks)\r
+ {\r
+ if (subtitleTrack.Default)\r
+ {\r
+ subtitleTrack.Default = false;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
#endregion\r
\r
#region Implemented Interfaces\r
this.AutomaticSubtitleSelection();\r
}\r
\r
+ /// <summary>\r
+ /// Checks the configuration of the subtitles and warns the user about any potential issues.\r
+ /// </summary>\r
+ public bool ValidateSubtitles()\r
+ {\r
+ var nonBurnedSubtitles = this.Task.SubtitleTracks.Where(subtitleTrack => !subtitleTrack.Burned).ToList();\r
+\r
+ if (nonBurnedSubtitles.Count > 0 && this.IsBurnableOnly)\r
+ {\r
+ MessageBoxResult result = this.errorService.ShowMessageBox(\r
+ Resources.Subtitles_WebmSubtitleIncompatibilityError,\r
+ Resources.Subtitles_WebmSubtitleIncompatibilityHeader,\r
+ MessageBoxButton.OKCancel,\r
+ MessageBoxImage.Warning);\r
+ if (result == MessageBoxResult.OK)\r
+ {\r
+ foreach (var subtitleTrack in nonBurnedSubtitles)\r
+ {\r
+ if (!subtitleTrack.Burned)\r
+ {\r
+ this.Remove(subtitleTrack);\r
+ }\r
+ }\r
+ }\r
+ else if (result == MessageBoxResult.Cancel)\r
+ {\r
+ return false;\r
+ }\r
+ else\r
+ {\r
+ return false;\r
+ }\r
+ }\r
+\r
+ return true;\r
+ }\r
+\r
#endregion\r
\r
#region Methods\r
{
return new List<OutputFormat>
{
- OutputFormat.Mp4, OutputFormat.Mkv
+ OutputFormat.Mp4, OutputFormat.Mkv, OutputFormat.WebM
};
}
}
this.Task.OutputFormat = value;
this.NotifyOfPropertyChange(() => this.SelectedOutputFormat);
this.NotifyOfPropertyChange(() => this.Task.OutputFormat);
- this.NotifyOfPropertyChange(() => this.IsMkv);
+ this.NotifyOfPropertyChange(() => this.IsMkvOrWebm);
this.SetExtension(string.Format(".{0}", this.Task.OutputFormat.ToString().ToLower()));
+ this.UpdateDisplayedInfo(); // output format may coreced to another due to container incompatibility
this.OnOutputFormatChanged(new OutputFormatChangedEventArgs(null));
this.OnTabStatusChanged(null);
}
/// <summary>
- /// Gets or sets a value indicating whether IsMkv.
+ /// Gets or sets a value indicating whether IsMkvOrWebm.
/// </summary>
- public bool IsMkv
+ public bool IsMkvOrWebm
{
get
{
- return this.SelectedOutputFormat == OutputFormat.Mkv;
+ return this.SelectedOutputFormat == OutputFormat.Mkv || this.SelectedOutputFormat == OutputFormat.WebM;
}
}
this.UpdateDisplayedInfo();
this.NotifyOfPropertyChange(() => this.SelectedOutputFormat);
- this.NotifyOfPropertyChange(() => this.IsMkv);
-
+ this.NotifyOfPropertyChange(() => this.IsMkvOrWebm);
+
this.NotifyOfPropertyChange(() => this.OptimizeMP4);
this.NotifyOfPropertyChange(() => this.IPod5GSupport);
this.NotifyOfPropertyChange(() => this.AlignAVStart);
}
// Now disable controls that are not required. The Following are for MP4 only!
- if (newExtension == ".mkv")
+ if (newExtension == ".mkv" || newExtension == ".webm")
{
this.OptimizeMP4 = false;
this.IPod5GSupport = false;
this.AlignAVStart = false;
}
- this.NotifyOfPropertyChange(() => this.IsMkv);
+ this.NotifyOfPropertyChange(() => this.IsMkvOrWebm);
// Update The browse file extension display
if (Path.HasExtension(newExtension))
{\r
this.NotifyOfPropertyChange(() => this.Task);\r
\r
- if ((Task.OutputFormat == OutputFormat.Mp4) && this.SelectedVideoEncoder == VideoEncoder.Theora)\r
+ VideoEncoder[] allowableWebmEncoders = { VideoEncoder.VP8, VideoEncoder.VP9 };\r
+\r
+ if ((Task.OutputFormat == OutputFormat.Mp4) && (this.SelectedVideoEncoder == VideoEncoder.Theora || allowableWebmEncoders.Contains(this.SelectedVideoEncoder)))\r
{\r
this.SelectedVideoEncoder = VideoEncoder.X264;\r
}\r
+\r
+ if ((Task.OutputFormat == OutputFormat.WebM) && !allowableWebmEncoders.Contains(this.SelectedVideoEncoder))\r
+ {\r
+ this.SelectedVideoEncoder = VideoEncoder.VP8;\r
+ }\r
}\r
\r
/// <summary>\r
xmlns:splitButton="clr-namespace:HandBrakeWPF.Controls.SplitButton"\r
xmlns:Properties="clr-namespace:HandBrakeWPF.Properties"\r
xmlns:subtitles="clr-namespace:HandBrakeWPF.Converters.Subtitles"\r
+ xmlns:viewModels="clr-namespace:HandBrakeWPF.ViewModels"\r
d:DesignHeight="350"\r
d:DesignWidth="500"\r
mc:Ignorable="d"\r
x:Name="subTab">\r
<UserControl.Resources>\r
<Converters:BooleanToVisibilityConverter x:Key="booleanToVisConverter" />\r
+ <Converters:BooleanConverter x:Key="booleanConverter" />\r
<subtitles:CanBurnSubtitleConverter x:Key="canBurnSubtitleConverter" />\r
\r
<Style x:Key="LongToolTip" TargetType="TextBlock">\r
</i:Interaction.Triggers>\r
</CheckBox>\r
\r
- <CheckBox Grid.Column="5" Margin="5,0,5,0" VerticalAlignment="Center" IsChecked="{Binding Default}" Content="{x:Static Properties:Resources.SubtitlesView_Default}" FontWeight="Bold">\r
+ <CheckBox Grid.Column="5" Margin="5,0,5,0" VerticalAlignment="Center" IsChecked="{Binding Default}" Content="{x:Static Properties:Resources.SubtitlesView_Default}" FontWeight="Bold"\r
+ IsEnabled="{Binding DataContext.IsBurnableOnly, Converter={StaticResource booleanConverter}, ConverterParameter=true, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">\r
<i:Interaction.Triggers>\r
<i:EventTrigger EventName="Click">\r
<cal:ActionMessage MethodName="SelectDefaultTrack">\r
</i:Interaction.Triggers>\r
</CheckBox>\r
\r
- <CheckBox Grid.Column="9" Margin="5,0,5,0" VerticalAlignment="Center" IsChecked="{Binding Default}" Content="Default" FontWeight="Bold">\r
+ <CheckBox Grid.Column="9" Margin="5,0,5,0" VerticalAlignment="Center" IsChecked="{Binding Default}" Content="Default" FontWeight="Bold"\r
+ IsEnabled="{Binding DataContext.IsBurnableOnly, Converter={StaticResource booleanConverter}, ConverterParameter=true, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">\r
<i:Interaction.Triggers>\r
<i:EventTrigger EventName="Click">\r
<cal:ActionMessage MethodName="SelectDefaultTrack">\r
Content="{x:Static Properties:Resources.MainView_WebOptimized}"
IsChecked="{Binding Path=OptimizeMP4}"
ToolTip="{x:Static Properties:ResourcesTooltips.MainView_Optimise}"
- Visibility="{Binding IsMkv, Converter={StaticResource boolToVisConverter}, ConverterParameter=true}" />
+ Visibility="{Binding IsMkvOrWebm, Converter={StaticResource boolToVisConverter}, ConverterParameter=true}" />
<CheckBox Name="AlignAVStart" VerticalAlignment="Center" Grid.Row="2" Grid.Column="1"
Content="{x:Static Properties:Resources.MainView_AlignAVStart}"
ToolTip="{x:Static Properties:ResourcesTooltips.MainView_AlignAVStart}"
IsChecked="{Binding Path=AlignAVStart}"
- Visibility="{Binding IsMkv, Converter={StaticResource boolToVisConverter}, ConverterParameter=true}" />
+ Visibility="{Binding IsMkvOrWebm, Converter={StaticResource boolToVisConverter}, ConverterParameter=true}" />
<CheckBox Name="iPod5G" VerticalAlignment="Center" Grid.Row="3" Grid.Column="1"
Content="{x:Static Properties:Resources.MainView_iPod5G}"
ToolTip="{x:Static Properties:ResourcesTooltips.MainView_IpodAtom}"
IsChecked="{Binding Path=IPod5GSupport}"
- Visibility="{Binding IsMkv, Converter={StaticResource boolToVisConverter}, ConverterParameter=true}" />
+ Visibility="{Binding IsMkvOrWebm, Converter={StaticResource boolToVisConverter}, ConverterParameter=true}" />
<TextBlock Text="Tracks:" FontWeight="Bold" Grid.Row="5" Grid.Column="0" VerticalAlignment="Center" />
<TextBlock Text="{Binding VideoTrackInfo}" Grid.Row="5" Grid.Column="1" VerticalAlignment="Center" />