瀏覽代碼

Automatically select preferred audio stream when playing video

Ivan Savenko 1 年之前
父節點
當前提交
37e196c565
共有 2 個文件被更改,包括 73 次插入23 次删除
  1. 49 2
      client/media/CVideoHandler.cpp
  2. 24 21
      lib/Languages.h

+ 49 - 2
client/media/CVideoHandler.cpp

@@ -23,6 +23,8 @@
 
 
 #include "../../lib/filesystem/CInputStream.h"
 #include "../../lib/filesystem/CInputStream.h"
 #include "../../lib/filesystem/Filesystem.h"
 #include "../../lib/filesystem/Filesystem.h"
+#include "../../lib/CGeneralTextHandler.h"
+#include "../../lib/Languages.h"
 
 
 #include <SDL_render.h>
 #include <SDL_render.h>
 
 
@@ -419,11 +421,56 @@ static FFMpegFormatDescription getAudioFormatProperties(int audioFormat)
 
 
 int FFMpegStream::findAudioStream() const
 int FFMpegStream::findAudioStream() const
 {
 {
+	std::vector<int> audioStreamIndices;
+
 	for(int i = 0; i < formatContext->nb_streams; i++)
 	for(int i = 0; i < formatContext->nb_streams; i++)
 		if(formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
 		if(formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
-			return i;
+			audioStreamIndices.push_back(i);
 
 
-	return -1;
+	if (audioStreamIndices.empty())
+		return -1;
+
+	if (audioStreamIndices.size() == 1)
+		return audioStreamIndices.front();
+
+	// multiple audio streams - try to pick best one based on language settings
+	std::map<int, std::string> streamToLanguage;
+
+	// Approach 1 - check if stream has language set in metadata
+	for (auto const & index : audioStreamIndices)
+	{
+		const AVDictionaryEntry *e = av_dict_get(formatContext->streams[index]->metadata, "language", nullptr, 0);
+		if (e)
+			streamToLanguage[index]	= e->value;
+	}
+
+	// Approach 2 - no metadata found. This may be video from Chronicles which have predefined (presumably hardcoded) list of languages
+	if (streamToLanguage.empty())
+	{
+		if (audioStreamIndices.size() == 2)
+		{
+			streamToLanguage[audioStreamIndices[0]] = Languages::getLanguageOptions(Languages::ELanguages::ENGLISH).tagISO2;
+			streamToLanguage[audioStreamIndices[1]] = Languages::getLanguageOptions(Languages::ELanguages::GERMAN).tagISO2;
+		}
+
+		if (audioStreamIndices.size() == 5)
+		{
+			streamToLanguage[audioStreamIndices[0]] = Languages::getLanguageOptions(Languages::ELanguages::ENGLISH).tagISO2;
+			streamToLanguage[audioStreamIndices[1]] = Languages::getLanguageOptions(Languages::ELanguages::FRENCH).tagISO2;
+			streamToLanguage[audioStreamIndices[2]] = Languages::getLanguageOptions(Languages::ELanguages::GERMAN).tagISO2;
+			streamToLanguage[audioStreamIndices[3]] = Languages::getLanguageOptions(Languages::ELanguages::ITALIAN).tagISO2;
+			streamToLanguage[audioStreamIndices[4]] = Languages::getLanguageOptions(Languages::ELanguages::SPANISH).tagISO2;
+		}
+	}
+
+	std::string preferredLanguageName = CGI->generaltexth->getPreferredLanguage();
+	std::string preferredTag = Languages::getLanguageOptions(preferredLanguageName).tagISO2;
+
+	for (auto const & entry : streamToLanguage)
+		if (entry.second == preferredTag)
+			return entry.first;
+
+	return audioStreamIndices.front();
 }
 }
 
 
 int FFMpegStream::findVideoStream() const
 int FFMpegStream::findVideoStream() const

+ 24 - 21
lib/Languages.h

@@ -68,6 +68,9 @@ struct Options
 	/// primary IETF language tag
 	/// primary IETF language tag
 	std::string tagIETF;
 	std::string tagIETF;
 
 
+	/// ISO 639-2 (B) language code
+	std::string tagISO2;
+
 	/// DateTime format
 	/// DateTime format
 	std::string dateTimeFormat;
 	std::string dateTimeFormat;
 
 
@@ -82,27 +85,27 @@ inline const auto & getLanguageList()
 {
 {
 	static const std::array<Options, 20> languages
 	static const std::array<Options, 20> languages
 	{ {
 	{ {
-		{ "czech",       "Czech",       "Čeština",    "CP1250", "cs", "%d.%m.%Y %T", EPluralForms::CZ_3, true },
-		{ "chinese",     "Chinese",     "简体中文",       "GBK",    "zh", "%F %T", EPluralForms::VI_1, true }, // Note: actually Simplified Chinese
-		{ "english",     "English",     "English",    "CP1252", "en", "%F %T", EPluralForms::EN_2, true }, // English uses international date/time format here
-		{ "finnish",     "Finnish",     "Suomi",      "CP1252", "fi", "%d.%m.%Y %T", EPluralForms::EN_2, true },
-		{ "french",      "French",      "Français",   "CP1252", "fr", "%d/%m/%Y %T", EPluralForms::FR_2, true },
-		{ "german",      "German",      "Deutsch",    "CP1252", "de", "%d.%m.%Y %T", EPluralForms::EN_2, true },
-		{ "hungarian",   "Hungarian",   "Magyar",     "CP1250", "hu", "%Y. %m. %d. %T", EPluralForms::EN_2, true },
-		{ "italian",     "Italian",     "Italiano",   "CP1250", "it", "%d/%m/%Y %T", EPluralForms::EN_2, true },
-		{ "korean",      "Korean",      "한국어",        "CP949",  "ko", "%F %T", EPluralForms::VI_1, true },
-		{ "polish",      "Polish",      "Polski",     "CP1250", "pl", "%d.%m.%Y %T", EPluralForms::PL_3, true },
-		{ "portuguese",  "Portuguese",  "Português",  "CP1252", "pt", "%d/%m/%Y %T", EPluralForms::EN_2, true }, // Note: actually Brazilian Portuguese
-		{ "russian",     "Russian",     "Русский",    "CP1251", "ru", "%d.%m.%Y %T", EPluralForms::UK_3, true },
-		{ "spanish",     "Spanish",     "Español",    "CP1252", "es", "%d/%m/%Y %T", EPluralForms::EN_2, true },
-		{ "swedish",     "Swedish",     "Svenska",    "CP1252", "sv", "%F %T", EPluralForms::EN_2, true },
-		{ "turkish",     "Turkish",     "Türkçe",     "CP1254", "tr", "%d.%m.%Y %T", EPluralForms::EN_2, true },
-		{ "ukrainian",   "Ukrainian",   "Українська", "CP1251", "uk", "%d.%m.%Y %T", EPluralForms::UK_3, true },
-		{ "vietnamese",  "Vietnamese",  "Tiếng Việt", "UTF-8",  "vi", "%d/%m/%Y %T", EPluralForms::VI_1, true }, // Fan translation uses special encoding
-
-		{ "other_cp1250", "Other (East European)",   "", "CP1250", "", "", EPluralForms::NONE, false },
-		{ "other_cp1251", "Other (Cyrillic Script)", "", "CP1251", "", "", EPluralForms::NONE, false },
-		{ "other_cp1252", "Other (West European)",   "", "CP1252", "", "", EPluralForms::NONE, false }
+		{ "czech",       "Czech",       "Čeština",    "CP1250", "cs", "cze", "%d.%m.%Y %T", EPluralForms::CZ_3, true },
+		{ "chinese",     "Chinese",     "简体中文",       "GBK",    "zh", "chi", "%F %T", EPluralForms::VI_1, true }, // Note: actually Simplified Chinese
+		{ "english",     "English",     "English",    "CP1252", "en", "eng", "%F %T", EPluralForms::EN_2, true }, // English uses international date/time format here
+		{ "finnish",     "Finnish",     "Suomi",      "CP1252", "fi", "fin", "%d.%m.%Y %T", EPluralForms::EN_2, true },
+		{ "french",      "French",      "Français",   "CP1252", "fr", "fre", "%d/%m/%Y %T", EPluralForms::FR_2, true },
+		{ "german",      "German",      "Deutsch",    "CP1252", "de", "ger", "%d.%m.%Y %T", EPluralForms::EN_2, true },
+		{ "hungarian",   "Hungarian",   "Magyar",     "CP1250", "hu", "hun", "%Y. %m. %d. %T", EPluralForms::EN_2, true },
+		{ "italian",     "Italian",     "Italiano",   "CP1250", "it", "ita", "%d/%m/%Y %T", EPluralForms::EN_2, true },
+		{ "korean",      "Korean",      "한국어",        "CP949",  "ko", "kor", "%F %T", EPluralForms::VI_1, true },
+		{ "polish",      "Polish",      "Polski",     "CP1250", "pl", "pol", "%d.%m.%Y %T", EPluralForms::PL_3, true },
+		{ "portuguese",  "Portuguese",  "Português",  "CP1252", "pt", "por", "%d/%m/%Y %T", EPluralForms::EN_2, true }, // Note: actually Brazilian Portuguese
+		{ "russian",     "Russian",     "Русский",    "CP1251", "ru", "rus", "%d.%m.%Y %T", EPluralForms::UK_3, true },
+		{ "spanish",     "Spanish",     "Español",    "CP1252", "es", "spa", "%d/%m/%Y %T", EPluralForms::EN_2, true },
+		{ "swedish",     "Swedish",     "Svenska",    "CP1252", "sv", "swe", "%F %T", EPluralForms::EN_2, true },
+		{ "turkish",     "Turkish",     "Türkçe",     "CP1254", "tr", "tur", "%d.%m.%Y %T", EPluralForms::EN_2, true },
+		{ "ukrainian",   "Ukrainian",   "Українська", "CP1251", "uk", "ukr", "%d.%m.%Y %T", EPluralForms::UK_3, true },
+		{ "vietnamese",  "Vietnamese",  "Tiếng Việt", "UTF-8",  "vi", "vie", "%d/%m/%Y %T", EPluralForms::VI_1, true }, // Fan translation uses special encoding
+
+		{ "other_cp1250", "Other (East European)",   "", "CP1250", "", "", "", EPluralForms::NONE, false },
+		{ "other_cp1251", "Other (Cyrillic Script)", "", "CP1251", "", "", "", EPluralForms::NONE, false },
+		{ "other_cp1252", "Other (West European)",   "", "CP1252", "", "", "", EPluralForms::NONE, false }
 	} };
 	} };
 	static_assert(languages.size() == static_cast<size_t>(ELanguages::COUNT), "Languages array is missing a value!");
 	static_assert(languages.size() == static_cast<size_t>(ELanguages::COUNT), "Languages array is missing a value!");