Skip to content

Commit

Permalink
Add WebM support (HandBrake#1822)
Browse files Browse the repository at this point in the history
Note that since webm has no official subtitle support, only burned in subtitles can be used with this muxer at this time.
  • Loading branch information
f3ndot authored and jstebbins committed Feb 13, 2019
1 parent 7cc7d56 commit d0e37ca
Show file tree
Hide file tree
Showing 31 changed files with 337 additions and 52 deletions.
2 changes: 1 addition & 1 deletion gtk/src/callbacks.c
Original file line number Diff line number Diff line change
Expand Up @@ -1934,7 +1934,7 @@ dvd_source_activate_cb(GtkWidget *widget, signal_user_data_t *ud)
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;
Expand Down
1 change: 1 addition & 0 deletions gtk/src/ghb.m4
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ filter_output([
<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"/>
Expand Down
58 changes: 56 additions & 2 deletions gtk/src/hb-backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -4298,6 +4298,8 @@ ghb_validate_video(GhbValue *settings, GtkWindow *parent)
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))
{
Expand All @@ -4306,6 +4308,21 @@ ghb_validate_video(GhbValue *settings, GtkWindow *parent)
_("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")))
{
Expand All @@ -4317,6 +4334,7 @@ ghb_validate_video(GhbValue *settings, GtkWindow *parent)
ghb_dict_set_string(settings, "VideoEncoder",
hb_video_encoder_get_short_name(vcodec));
}

return TRUE;
}

Expand All @@ -4340,6 +4358,12 @@ ghb_validate_subtitles(GhbValue *settings, GtkWindow *parent)
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++)
Expand Down Expand Up @@ -4369,6 +4393,22 @@ ghb_validate_subtitles(GhbValue *settings, GtkWindow *parent)
{
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;
Expand Down Expand Up @@ -4457,15 +4497,19 @@ ghb_validate_audio(GhbValue *settings, GtkWindow *parent)
{
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";
Expand All @@ -4476,6 +4520,16 @@ ghb_validate_audio(GhbValue *settings, GtkWindow *parent)
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(
Expand Down
5 changes: 5 additions & 0 deletions gtk/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,7 @@ ghb_activate_cb(GApplication * app, signal_user_data_t * ud)
SourceFilterEVO
SourceFilterVOB
SourceFilterMKV
SourceFilterWebM
SourceFilterMP4
SourceFilterAVI
SourceFilterMOV
Expand Down Expand Up @@ -1174,6 +1175,10 @@ ghb_activate_cb(GApplication * app, signal_user_data_t * ud)
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");
Expand Down
29 changes: 18 additions & 11 deletions libhb/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ enum
HB_GID_ACODEC_OPUS,
HB_GID_MUX_MKV,
HB_GID_MUX_MP4,
HB_GID_MUX_WEBM,
};

#define HB_VIDEO_CLOCK 27000000 // 27MHz clock
Expand Down Expand Up @@ -265,8 +266,8 @@ hb_encoder_internal_t hb_video_encoders[] =
{ { "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]);
Expand Down Expand Up @@ -375,11 +376,11 @@ hb_encoder_internal_t hb_audio_encoders[] =
{ { "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]);
Expand Down Expand Up @@ -443,14 +444,15 @@ hb_container_t *hb_containers_last_item = NULL;
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)
Expand All @@ -459,6 +461,7 @@ 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:
Expand Down Expand Up @@ -4961,6 +4964,10 @@ int hb_subtitle_can_pass( int source, int mux )
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);
Expand Down
26 changes: 15 additions & 11 deletions libhb/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -606,18 +606,22 @@ struct hb_job_s
* 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;
Expand Down
2 changes: 1 addition & 1 deletion libhb/decavcodec.c
Original file line number Diff line number Diff line change
Expand Up @@ -853,7 +853,7 @@ static int decavcodecaBSInfo( hb_work_object_t *w, const hb_buffer_t *buf,
{
// 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);
}

Expand Down
1 change: 1 addition & 0 deletions libhb/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ typedef struct hb_mux_data_s hb_mux_data_t;

DECLARE_MUX( mp4 );
DECLARE_MUX( mkv );
DECLARE_MUX( webm );
DECLARE_MUX( avformat );

void hb_deinterlace(hb_buffer_t *dst, hb_buffer_t *src);
Expand Down
14 changes: 13 additions & 1 deletion libhb/muxavformat.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ enum
{
META_MUX_MP4,
META_MUX_MKV,
META_MUX_WEBM,
META_MUX_LAST
};

Expand Down Expand Up @@ -98,6 +99,7 @@ static char* lookup_lang_code(int mux, char *iso639_2)
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 );
Expand Down Expand Up @@ -164,6 +166,15 @@ static int avformatInit( hb_mux_object_t * m )
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);
Expand Down Expand Up @@ -1184,7 +1195,8 @@ static int avformatMux(hb_mux_object_t *m, hb_mux_data_t *track, hb_buffer_t *bu
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
Expand Down
1 change: 1 addition & 0 deletions libhb/muxcommon.c
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,7 @@ static int muxInit( hb_work_object_t * muxer, hb_job_t * job )
{
case HB_MUX_AV_MP4:
case HB_MUX_AV_MKV:
case HB_MUX_AV_WEBM:
mux->m = hb_mux_avformat_init( job );
break;
default:
Expand Down
2 changes: 1 addition & 1 deletion macosx/HBAudioTrack.m
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ - (int)sanatizeEncoderValue:(int)proposedEncoder
}
}

return HB_ACODEC_CA_AAC;
return hb_audio_encoder_get_default(self.container);
}
else
{
Expand Down
12 changes: 12 additions & 0 deletions macosx/HBJob+UIAdditions.m
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ - (NSArray *)containers
{
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);
Expand Down Expand Up @@ -922,6 +926,10 @@ - (id)transformedValue:(id)value
{
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);
Expand Down Expand Up @@ -951,6 +959,10 @@ - (id)reverseTransformedValue:(id)value
{
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]));
}
Expand Down
4 changes: 4 additions & 0 deletions macosx/HBPreviewGenerator.m
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,10 @@ - (BOOL) createMovieAsyncWithImageAtIndex: (NSUInteger) index duration: (NSUInte
{
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)
Expand Down
2 changes: 1 addition & 1 deletion macosx/HBVideo.m
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ - (void)containerChanged

if (!encoderSupported)
{
self.encoder = HB_VCODEC_X264;
self.encoder = hb_video_encoder_get_default(self.job.container);
}
}

Expand Down
3 changes: 3 additions & 0 deletions macosx/HandBrakeKit/de.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@
/* HBJob -> Format display name */
"MKV File" = "MKV-Datei";

/* HBJob -> Format display name */
"WebM File" = "WebM-Datei";

/* HBJob -> Format display name */
"MP4 File" = "MP4-Datei";

Expand Down
Loading

0 comments on commit d0e37ca

Please sign in to comment.