Add HLS audio language support
Before this, HLS output did not contain language information. Now, media playlists are properly tagged with a language in the master playlist. b/36134267 Change-Id: I172e946dbedd096a44cb2f917b007cc004756228
This commit is contained in:
parent
c6cbd73465
commit
924d6d4693
|
@ -11,7 +11,7 @@
|
|||
#include "packager/base/strings/string_number_conversions.h"
|
||||
#include "packager/base/strings/string_split.h"
|
||||
#include "packager/media/base/container_names.h"
|
||||
#include "packager/mpd/base/language_utils.h"
|
||||
#include "packager/media/base/language_utils.h"
|
||||
|
||||
namespace shaka {
|
||||
namespace media {
|
||||
|
|
|
@ -121,8 +121,18 @@ bool MasterPlaylist::WriteMasterPlaylist(const std::string& base_url,
|
|||
for (const MediaPlaylist* audio_playlist : audio_playlists) {
|
||||
base::StringAppendF(
|
||||
&audio_output,
|
||||
"#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"%s\",NAME=\"%s\",URI=\"%s\"\n",
|
||||
group_id.c_str(), audio_playlist->name().c_str(),
|
||||
"#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"%s\",NAME=\"%s\",",
|
||||
group_id.c_str(), audio_playlist->name().c_str());
|
||||
std::string language = audio_playlist->GetLanguage();
|
||||
if (!language.empty()) {
|
||||
base::StringAppendF(
|
||||
&audio_output,
|
||||
"LANGUAGE=\"%s\",",
|
||||
language.c_str());
|
||||
}
|
||||
base::StringAppendF(
|
||||
&audio_output,
|
||||
"URI=\"%s\"\n",
|
||||
(base_url + audio_playlist->file_name()).c_str());
|
||||
const uint64_t audio_bitrate = audio_playlist->Bitrate();
|
||||
if (audio_bitrate > max_audio_bitrate)
|
||||
|
|
|
@ -130,6 +130,7 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) {
|
|||
std::string audio_codec = "audiocodec";
|
||||
MockMediaPlaylist english_playlist(kVodPlaylist, "eng.m3u8", "english",
|
||||
"audiogroup");
|
||||
EXPECT_CALL(english_playlist, GetLanguage()).WillRepeatedly(Return("en"));
|
||||
english_playlist.SetStreamTypeForTesting(
|
||||
MediaPlaylist::MediaPlaylistStreamType::kPlayListAudio);
|
||||
english_playlist.SetCodecForTesting(audio_codec);
|
||||
|
@ -141,6 +142,7 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) {
|
|||
// Second audio, spanish.m3u8.
|
||||
MockMediaPlaylist spanish_playlist(kVodPlaylist, "spa.m3u8", "espanol",
|
||||
"audiogroup");
|
||||
EXPECT_CALL(spanish_playlist, GetLanguage()).WillRepeatedly(Return("es"));
|
||||
spanish_playlist.SetStreamTypeForTesting(
|
||||
MediaPlaylist::MediaPlaylistStreamType::kPlayListAudio);
|
||||
spanish_playlist.SetCodecForTesting(audio_codec);
|
||||
|
@ -166,9 +168,9 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) {
|
|||
"## Generated with https://github.com/google/shaka-packager version "
|
||||
"test\n"
|
||||
"#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"audiogroup\",NAME=\"english\","
|
||||
"URI=\"http://playlists.org/eng.m3u8\"\n"
|
||||
"LANGUAGE=\"en\",URI=\"http://playlists.org/eng.m3u8\"\n"
|
||||
"#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"audiogroup\",NAME=\"espanol\","
|
||||
"URI=\"http://playlists.org/spa.m3u8\"\n"
|
||||
"LANGUAGE=\"es\",URI=\"http://playlists.org/spa.m3u8\"\n"
|
||||
"#EXT-X-STREAM-INF:AUDIO=\"audiogroup\","
|
||||
"CODECS=\"sdvideocodec,audiocodec\","
|
||||
"BANDWIDTH=360000\n"
|
||||
|
@ -198,6 +200,7 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
|
|||
std::string audio_codec_lo = "audiocodec_lo";
|
||||
MockMediaPlaylist eng_lo_playlist(kVodPlaylist, "eng_lo.m3u8", "english_lo",
|
||||
"audio_lo");
|
||||
EXPECT_CALL(eng_lo_playlist, GetLanguage()).WillRepeatedly(Return("en"));
|
||||
eng_lo_playlist.SetStreamTypeForTesting(
|
||||
MediaPlaylist::MediaPlaylistStreamType::kPlayListAudio);
|
||||
eng_lo_playlist.SetCodecForTesting(audio_codec_lo);
|
||||
|
@ -209,6 +212,7 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
|
|||
std::string audio_codec_hi = "audiocodec_hi";
|
||||
MockMediaPlaylist eng_hi_playlist(kVodPlaylist, "eng_hi.m3u8", "english_hi",
|
||||
"audio_hi");
|
||||
EXPECT_CALL(eng_hi_playlist, GetLanguage()).WillRepeatedly(Return("en"));
|
||||
eng_hi_playlist.SetStreamTypeForTesting(
|
||||
MediaPlaylist::MediaPlaylistStreamType::kPlayListAudio);
|
||||
eng_hi_playlist.SetCodecForTesting(audio_codec_hi);
|
||||
|
@ -233,9 +237,9 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
|
|||
"## Generated with https://github.com/google/shaka-packager version "
|
||||
"test\n"
|
||||
"#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"audio_hi\",NAME=\"english_hi\","
|
||||
"URI=\"http://anydomain.com/eng_hi.m3u8\"\n"
|
||||
"LANGUAGE=\"en\",URI=\"http://anydomain.com/eng_hi.m3u8\"\n"
|
||||
"#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"audio_lo\",NAME=\"english_lo\","
|
||||
"URI=\"http://anydomain.com/eng_lo.m3u8\"\n"
|
||||
"LANGUAGE=\"en\",URI=\"http://anydomain.com/eng_lo.m3u8\"\n"
|
||||
"#EXT-X-STREAM-INF:AUDIO=\"audio_hi\","
|
||||
"CODECS=\"videocodec,audiocodec_hi\","
|
||||
"BANDWIDTH=400000\n"
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "packager/base/logging.h"
|
||||
#include "packager/base/strings/stringprintf.h"
|
||||
#include "packager/media/base/language_utils.h"
|
||||
#include "packager/media/file/file.h"
|
||||
#include "packager/version/version.h"
|
||||
|
||||
|
@ -344,5 +345,20 @@ bool MediaPlaylist::SetTargetDuration(uint32_t target_duration) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Duplicated from MpdUtils because:
|
||||
// 1. MpdUtils header depends on libxml header, which is not in the deps here
|
||||
// 2. GetLanguage depends on MediaInfo from packager/mpd/
|
||||
// 3. Moving GetLanguage to LanguageUtils would create a a media => mpd dep.
|
||||
// TODO: fix this dependency situation and factor this out to a common location
|
||||
std::string MediaPlaylist::GetLanguage() const {
|
||||
std::string lang;
|
||||
if (media_info_.has_audio_info()) {
|
||||
lang = media_info_.audio_info().language();
|
||||
} else if (media_info_.has_text_info()) {
|
||||
lang = media_info_.text_info().language();
|
||||
}
|
||||
return LanguageToShortestForm(lang);
|
||||
}
|
||||
|
||||
} // namespace hls
|
||||
} // namespace shaka
|
||||
|
|
|
@ -149,6 +149,10 @@ class MediaPlaylist {
|
|||
/// @return true if set, false otherwise.
|
||||
virtual bool SetTargetDuration(uint32_t target_duration);
|
||||
|
||||
/// @return the language of the media, as an ISO language tag in its shortest
|
||||
/// form. May be an empty string for video.
|
||||
virtual std::string GetLanguage() const;
|
||||
|
||||
private:
|
||||
// Mainly for MasterPlaylist to use these values.
|
||||
const std::string file_name_;
|
||||
|
|
|
@ -352,5 +352,23 @@ TEST_F(MediaPlaylistTest, RemoveOldestSegment) {
|
|||
EXPECT_TRUE(media_playlist_.WriteToFile(&file));
|
||||
}
|
||||
|
||||
TEST_F(MediaPlaylistTest, GetLanguage) {
|
||||
MediaInfo media_info;
|
||||
media_info.set_reference_time_scale(90000);
|
||||
|
||||
// Check conversions from long to short form.
|
||||
media_info.mutable_audio_info()->set_language("eng");
|
||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(media_info));
|
||||
EXPECT_EQ("en", media_playlist_.GetLanguage()); // short form
|
||||
|
||||
media_info.mutable_audio_info()->set_language("eng-US");
|
||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(media_info));
|
||||
EXPECT_EQ("en-US", media_playlist_.GetLanguage()); // region preserved
|
||||
|
||||
media_info.mutable_audio_info()->set_language("apa");
|
||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(media_info));
|
||||
EXPECT_EQ("apa", media_playlist_.GetLanguage()); // no short form exists
|
||||
}
|
||||
|
||||
} // namespace hls
|
||||
} // namespace shaka
|
||||
|
|
|
@ -40,6 +40,7 @@ class MockMediaPlaylist : public MediaPlaylist {
|
|||
MOCK_CONST_METHOD0(Bitrate, uint64_t());
|
||||
MOCK_CONST_METHOD0(GetLongestSegmentDuration, double());
|
||||
MOCK_METHOD1(SetTargetDuration, bool(uint32_t target_duration));
|
||||
MOCK_CONST_METHOD0(GetLanguage, std::string());
|
||||
};
|
||||
|
||||
} // namespace hls
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#include "packager/mpd/base/language_utils.h"
|
||||
#include "packager/media/base/language_utils.h"
|
||||
|
||||
#include "packager/base/logging.h"
|
||||
|
||||
|
@ -87,6 +87,11 @@ void SplitLanguageTag(const std::string& tag,
|
|||
namespace shaka {
|
||||
|
||||
std::string LanguageToShortestForm(const std::string& language) {
|
||||
// Do not try to mangle blank strings.
|
||||
if (language.size() == 0) {
|
||||
return language;
|
||||
}
|
||||
|
||||
std::string main_language;
|
||||
std::string subtag;
|
||||
SplitLanguageTag(language, &main_language, &subtag);
|
|
@ -6,16 +6,19 @@
|
|||
//
|
||||
// Funtions used by MpdBuilder class to generate an MPD file.
|
||||
|
||||
#ifndef MPD_BASE_LANGUAGE_UTILS_H_
|
||||
#define MPD_BASE_LANGUAGE_UTILS_H_
|
||||
#ifndef MEDIA_BASE_LANGUAGE_UTILS_H_
|
||||
#define MEDIA_BASE_LANGUAGE_UTILS_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace shaka {
|
||||
|
||||
class MediaInfo;
|
||||
|
||||
/// Convert a language tag to its shortest form, as required by RFC 5646
|
||||
/// indicated in the MPD spec. Assumes the input is a valid ISO-639-2 or
|
||||
/// ISO-639-1 language tag. Regions and variants are not supported.
|
||||
/// indicated in the MPD and HLS specs. Assumes the input is a valid ISO-639-2
|
||||
/// or ISO-639-1 language tag, or an empty string. Regions and variants are
|
||||
/// preserved in the conversion.
|
||||
std::string LanguageToShortestForm(const std::string& language);
|
||||
|
||||
/// Convert a language tag to a 3-letter ISO-639-2 code, as required by the ISO
|
||||
|
@ -25,4 +28,4 @@ std::string LanguageToISO_639_2(const std::string& language);
|
|||
|
||||
} // namespace shaka
|
||||
|
||||
#endif // MPD_BASE_LANGUAGE_UTILS_H_
|
||||
#endif // MEDIA_BASE_LANGUAGE_UTILS_H_
|
|
@ -52,6 +52,8 @@
|
|||
'key_fetcher.h',
|
||||
'key_source.cc',
|
||||
'key_source.h',
|
||||
'language_utils.cc',
|
||||
'language_utils.h',
|
||||
'limits.h',
|
||||
'macros.h',
|
||||
'media_handler.cc',
|
||||
|
|
|
@ -24,9 +24,9 @@
|
|||
#include "packager/base/synchronization/lock.h"
|
||||
#include "packager/base/time/default_clock.h"
|
||||
#include "packager/base/time/time.h"
|
||||
#include "packager/media/base/language_utils.h"
|
||||
#include "packager/media/file/file.h"
|
||||
#include "packager/mpd/base/content_protection_element.h"
|
||||
#include "packager/mpd/base/language_utils.h"
|
||||
#include "packager/mpd/base/mpd_utils.h"
|
||||
#include "packager/mpd/base/xml/xml_node.h"
|
||||
#include "packager/version/version.h"
|
||||
|
|
|
@ -39,8 +39,6 @@
|
|||
'base/content_protection_element.h',
|
||||
'base/dash_iop_mpd_notifier.cc',
|
||||
'base/dash_iop_mpd_notifier.h',
|
||||
'base/language_utils.cc',
|
||||
'base/language_utils.h',
|
||||
'base/mpd_builder.cc',
|
||||
'base/mpd_builder.h',
|
||||
'base/mpd_notifier_util.cc',
|
||||
|
|
Loading…
Reference in New Issue