From 03d11dcf68da6fba2353e9daf850a89ca56f4fe5 Mon Sep 17 00:00:00 2001 From: Dawid Juszczak Date: Thu, 26 Sep 2019 16:23:22 +0200 Subject: [PATCH] [mediacontroller] add api for new metadata features [ACR] http://suprem.sec.samsung.net/jira/browse/TWDAPI-238 [Verification] tested manually on chrome console tct-mediacontroller-tizen-tests 100% PASS Change-Id: I459724bfa88ab9a37a97be55eda0bd2e01e83add Signed-off-by: Dawid Juszczak --- src/mediacontroller/mediacontroller_api.js | 64 +++++- src/mediacontroller/mediacontroller_server.cc | 27 ++- src/mediacontroller/mediacontroller_utils.cc | 196 ++++++++++++++++-- src/mediacontroller/mediacontroller_utils.h | 3 + 4 files changed, 264 insertions(+), 26 deletions(-) diff --git a/src/mediacontroller/mediacontroller_api.js b/src/mediacontroller/mediacontroller_api.js index 71bfaf61..2e8576e6 100755 --- a/src/mediacontroller/mediacontroller_api.js +++ b/src/mediacontroller/mediacontroller_api.js @@ -423,6 +423,12 @@ var MediaControllerMetadata = function(data) { var _description = ''; var _trackNum = ''; var _picture = ''; + var _seasonNumber = 0; + var _seasonTitle = null; + var _episodeNumber = 0; + var _episodeTitle = null; + var _resolutionWidth = 0; + var _resolutionHeight = 0; Object.defineProperties(this, { title: { get: function() { @@ -522,6 +528,62 @@ var MediaControllerMetadata = function(data) { _picture = converter_.toString(v); }, enumerable: true + }, + seasonNumber: { + get: function() { + return _seasonNumber; + }, + set: function(v) { + _seasonNumber = converter_.toLong(v); + }, + enumerable: true + }, + seasonTitle: { + get: function() { + return _seasonTitle; + }, + set: function(v) { + _seasonTitle = converter_.toString(v, true); + }, + enumerable: true, + nullable: true + }, + episodeNumber: { + get: function() { + return _episodeNumber; + }, + set: function(v) { + _episodeNumber = converter_.toLong(v); + }, + enumerable: true + }, + episodeTitle: { + get: function() { + return _episodeTitle; + }, + set: function(v) { + _episodeTitle = converter_.toString(v, true); + }, + enumerable: true, + nullable: true + }, + resolutionWidth: { + get: function() { + return _resolutionWidth; + }, + set: function(v) { + _resolutionWidth = v < 0 ? _resolutionWidth : converter_.toLong(v); + }, + enumerable: true + }, + resolutionHeight: { + get: function() { + return _resolutionHeight; + }, + set: function(v) { + _resolutionHeight = v < 0 ? _resolutionHeight : converter_.toLong(v); + }, + enumerable: true } }); @@ -3592,7 +3654,7 @@ MediaControllerPlaylist.prototype.addItem = function(index, metadata) { var data = { index: args.index, - metadata: args.metadata, + metadata: new MediaControllerMetadata(args.metadata), name: this.name }; diff --git a/src/mediacontroller/mediacontroller_server.cc b/src/mediacontroller/mediacontroller_server.cc index 1d86a527..2f7629e7 100644 --- a/src/mediacontroller/mediacontroller_server.cc +++ b/src/mediacontroller/mediacontroller_server.cc @@ -295,9 +295,15 @@ PlatformResult MediaControllerServer::SetRepeatState(const std::string& state) { PlatformResult MediaControllerServer::SetMetadata(const picojson::object& metadata) { ScopeLogger(); - for (picojson::object::const_iterator i = metadata.begin(); i != metadata.end(); ++i) { + picojson::object encoded; + PlatformResult result = utils::EncodeMetadata(metadata, &encoded); + if (!result) { + return result; + } + + for (picojson::object::const_iterator i = encoded.begin(); i != encoded.end(); ++i) { mc_meta_e attr_e; - PlatformResult result = types::MediaControllerMetadataAttributeEnum.getValue(i->first, &attr_e); + result = types::MediaControllerMetadataAttributeEnum.getValue(i->first, &attr_e); if (!result) { return result; } @@ -613,17 +619,18 @@ PlatformResult MediaControllerServer::MediaControllerPlaylistAddItem( return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Playlist with given name doesn't exist"); } - for (const auto& v : metadata) { - LoggerD("Key: %s - Value: %s ", v.first.c_str(), v.second.to_str().c_str()); + picojson::object encoded; + PlatformResult result = utils::EncodeMetadata(metadata, &encoded); + if (!result) { + return result; + } - mc_meta_e attr_e; - PlatformResult result = types::MediaControllerMetadataAttributeEnum.getValue(v.first, &attr_e); - if (!result) { - return result; - } + for (auto v : types::MediaControllerMetadataAttributeEnum) { + std::string value = encoded[v.first].get(); + LoggerD("Key: %s - Value: %s", v.first.c_str(), value.c_str()); int ret = mc_server_add_item_to_playlist(handle_, playlist_handle_map_[name], index.c_str(), - attr_e, v.second.to_str().c_str()); + v.second, value.c_str()); if (MEDIA_CONTROLLER_ERROR_NONE != ret) { return LogAndCreateResult( ErrorCode::UNKNOWN_ERR, "Error adding playlist item", diff --git a/src/mediacontroller/mediacontroller_utils.cc b/src/mediacontroller/mediacontroller_utils.cc index 28fd4458..7c5507e1 100644 --- a/src/mediacontroller/mediacontroller_utils.cc +++ b/src/mediacontroller/mediacontroller_utils.cc @@ -61,7 +61,10 @@ const common::PlatformEnum MediaControllerMetadataAttributeEnum{ {"copyright", MC_META_MEDIA_COPYRIGHT}, {"description", MC_META_MEDIA_DESCRIPTION}, {"trackNum", MC_META_MEDIA_TRACK_NUM}, - {"picture", MC_META_MEDIA_PICTURE}}; + {"picture", MC_META_MEDIA_PICTURE}, + {"season", MC_META_MEDIA_SEASON}, + {"episode", MC_META_MEDIA_EPISODE}, + {"resolution", MC_META_MEDIA_RESOLUTION}}; const common::PlatformEnum MediaControllerRepeatModeEnum{ {"REPEAT_OFF", MC_REPEAT_MODE_OFF}, @@ -126,7 +129,7 @@ const common::PlatformEnum MediaControllerDisplayRotation {"ROTATION_180", MC_DISPLAY_ROTATION_180}, {"ROTATION_270", MC_DISPLAY_ROTATION_270}}; -PlatformResult ConvertPlaybackState(mc_playback_h playback_h, std::string* state) { +PlatformResult ConvertPlaybackState(mc_playback_h playback_h, std::string *state) { ScopeLogger(); int ret; @@ -151,7 +154,7 @@ PlatformResult ConvertPlaybackState(mc_playback_h playback_h, std::string* state return PlatformResult(ErrorCode::NO_ERROR); } -PlatformResult ConvertContentAgeRating(mc_playback_h playback_h, std::string* rating) { +PlatformResult ConvertContentAgeRating(mc_playback_h playback_h, std::string *rating) { ScopeLogger(); mc_content_age_rating_e rating_e = MC_CONTENT_RATING_ALL; @@ -172,7 +175,7 @@ PlatformResult ConvertContentAgeRating(mc_playback_h playback_h, std::string* ra return PlatformResult(ErrorCode::NO_ERROR); } -PlatformResult ConvertContentType(mc_playback_h playback_h, std::string* contentType) { +PlatformResult ConvertContentType(mc_playback_h playback_h, std::string *contentType) { ScopeLogger(); mc_content_type_e content_type_e = MC_CONTENT_TYPE_UNDECIDED; int ret = mc_client_get_playback_content_type(playback_h, &content_type_e); @@ -191,7 +194,7 @@ PlatformResult ConvertContentType(mc_playback_h playback_h, std::string* content return PlatformResult(ErrorCode::NO_ERROR); } -PlatformResult ConvertPlaybackPosition(mc_playback_h playback_h, double* position) { +PlatformResult ConvertPlaybackPosition(mc_playback_h playback_h, double *position) { ScopeLogger(); int ret; @@ -209,14 +212,17 @@ PlatformResult ConvertPlaybackPosition(mc_playback_h playback_h, double* positio return PlatformResult(ErrorCode::NO_ERROR); } -PlatformResult ConvertMetadata(mc_metadata_h metadata_h, picojson::object* metadata) { +PlatformResult ConvertMetadata(mc_metadata_h metadata_h, picojson::object *metadata) { ScopeLogger(); - char* value = nullptr; + char *value = nullptr; SCOPE_EXIT { free(value); }; + picojson::value to_decode = picojson::value(picojson::object()); + picojson::object &obj = to_decode.get(); + for (auto entry : MediaControllerMetadataAttributeEnum) { int ret = mc_metadata_get(metadata_h, entry.second, &value); if (ret != MEDIA_CONTROLLER_ERROR_NONE) { @@ -225,14 +231,19 @@ PlatformResult ConvertMetadata(mc_metadata_h metadata_h, picojson::object* metad ret, get_error_message(ret))); } - (*metadata)[entry.first] = picojson::value(std::string(value ? value : "")); + obj[entry.first] = picojson::value(value); + } + + PlatformResult result = utils::DecodeMetadata(to_decode, metadata); + if (!result) { + return result; } return PlatformResult(ErrorCode::NO_ERROR); } -PlatformResult ConvertPlaybackAbility(mc_playback_ability_h ability_h, const std::string& action, - std::string* ability_str) { +PlatformResult ConvertPlaybackAbility(mc_playback_ability_h ability_h, const std::string &action, + std::string *ability_str) { ScopeLogger(); mc_ability_support_e ability_e; @@ -268,11 +279,11 @@ PlatformResult ConvertPlaybackAbility(mc_playback_ability_h ability_h, const std } // namespace types -PlatformResult utils::GetAllPlaylists(const std::string& app_id, picojson::array* playlists) { +PlatformResult utils::GetAllPlaylists(const std::string &app_id, picojson::array *playlists) { ScopeLogger(); - auto OnPlaylists = [](mc_playlist_h playlist, void* user_data) -> bool { - char* name = nullptr; + auto OnPlaylists = [](mc_playlist_h playlist, void *user_data) -> bool { + char *name = nullptr; SCOPE_EXIT { free(name); @@ -284,10 +295,10 @@ PlatformResult utils::GetAllPlaylists(const std::string& app_id, picojson::array return false; } - auto playlists = static_cast(user_data); + auto playlists = static_cast(user_data); picojson::value value = picojson::value(picojson::object()); - picojson::object& obj = value.get(); + picojson::object &obj = value.get(); obj.insert(std::make_pair("name", picojson::value{name})); playlists->push_back(value); @@ -321,5 +332,160 @@ ErrorCode utils::ConvertMediaControllerError(int e) { return error; } +bool isEncodable(std::string name) { + if (name == "season" || name == "episode" || name == "resolution") { + return true; + } + return false; +} + +PlatformResult utils::EncodeMetadata(const picojson::object &to_encode, picojson::object *encoded) { + ScopeLogger(); + + int ret = 0; + char *encoded_value = nullptr; + int number = 0; + + SCOPE_EXIT { + free(encoded_value); + }; + + // season + try { + number = static_cast(to_encode.at("seasonNumber").get()); + const char *season_title = to_encode.at("seasonTitle").is() + ? nullptr + : to_encode.at("seasonTitle").get().c_str(); + + ret = mc_metadata_encode_season(number, season_title, &encoded_value); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + return LogAndCreateResult( + ErrorCode::UNKNOWN_ERR, "Error while encoding season", + ("mc_metadata_encode_season() error: %d, message: %s", ret, get_error_message(ret))); + } + + (*encoded).insert(std::make_pair("season", picojson::value(encoded_value))); + } catch (...) { + LoggerE("Season formation missing! - This should never happened."); + } + + // episode + try { + number = static_cast(to_encode.at("episodeNumber").get()); + const char *episode_title = to_encode.at("episodeTitle").is() + ? nullptr + : to_encode.at("episodeTitle").get().c_str(); + + ret = mc_metadata_encode_episode(number, episode_title, &encoded_value); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + return LogAndCreateResult( + ErrorCode::UNKNOWN_ERR, "Error while encoding episode", + ("mc_metadata_encode_episode() error: %d, message: %s", ret, get_error_message(ret))); + } + + (*encoded).insert(std::make_pair("episode", picojson::value(encoded_value))); + } catch (...) { + LoggerE("Episode formation missing! - This should never happened."); + } + + // resolution + try { + u_int width = static_cast(to_encode.at("resolutionWidth").get()); + u_int height = static_cast(to_encode.at("resolutionHeight").get()); + + ret = mc_metadata_encode_resolution(width, height, &encoded_value); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + return LogAndCreateResult( + ErrorCode::UNKNOWN_ERR, "Error while encoding resolution", + ("mc_metadata_encode_resolution() error: %d, message: %s", ret, get_error_message(ret))); + } + + (*encoded).insert(std::make_pair("resolution", picojson::value(encoded_value))); + } catch (...) { + LoggerE("Resolution formation missing! - This should never happened."); + } + + // other metadata attributes + for (auto entry : types::MediaControllerMetadataAttributeEnum) { + std::string name = entry.first; + if (name != "season" && name != "episode" && name != "resolution") { + try { + std::string value_str = to_encode.at(entry.first).get(); + (*encoded).insert(std::make_pair(entry.first, picojson::value(value_str))); + } catch (...) { + LoggerE("%s formation missing! - This should never happened.", name.c_str()); + } + } + } + + return PlatformResult(ErrorCode::NO_ERROR); +} + +PlatformResult utils::DecodeMetadata(const picojson::value &to_decode, picojson::object *decoded) { + ScopeLogger(); + + int ret = 0; + int number = 0; + char *season_title = nullptr; + char *episode_title = nullptr; + + SCOPE_EXIT { + free(season_title); + free(episode_title); + }; + + // decode season + std::string val = to_decode.get("season").get(); + + ret = mc_metadata_decode_season(val.c_str(), &number, &season_title); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + return LogAndCreateResult( + ErrorCode::UNKNOWN_ERR, "Error while decoding season", + ("mc_metadata_decode_season() error: %d, message: %s", ret, get_error_message(ret))); + } + + (*decoded)["seasonNumber"] = picojson::value((double)number); + (*decoded)["episodeTitle"] = season_title ? picojson::value(std::string(season_title)) : picojson::value(); + + // decode episode + val = to_decode.get("episode").get(); + + ret = mc_metadata_decode_episode(val.c_str(), &number, &episode_title); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + return LogAndCreateResult( + ErrorCode::UNKNOWN_ERR, "Error while decoding episode", + ("mc_metadata_decode_episode() error: %d, message: %s", ret, get_error_message(ret))); + } + + (*decoded)["episodeNumber"] = picojson::value((double)number); + (*decoded)["episodeTitle"] = episode_title ? picojson::value(std::string(episode_title)) : picojson::value(); + + // decode resolution + val = to_decode.get("resolution").get(); + u_int width = 0; + u_int height = 0; + + ret = mc_metadata_decode_resolution(val.c_str(), &width, &height); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + return LogAndCreateResult( + ErrorCode::UNKNOWN_ERR, "Error while decoding resolution", + ("mc_metadata_decode_resolution() error: %d, message: %s", ret, get_error_message(ret))); + } + + (*decoded)["resolutionWidth"] = picojson::value((double)width); + (*decoded)["resolutionHeight"] = picojson::value((double)height); + + // now rewrite not encoded attributes + for (auto entry : types::MediaControllerMetadataAttributeEnum) { + std::string name = entry.first; + // to omit decoded attributes + if (!isEncodable(name)) { + (*decoded)[name] = to_decode.get(name); + } + } + + return PlatformResult(ErrorCode::NO_ERROR); +} + } // namespace mediacontroller } // namespace extension diff --git a/src/mediacontroller/mediacontroller_utils.h b/src/mediacontroller/mediacontroller_utils.h index 96556595..3922c433 100644 --- a/src/mediacontroller/mediacontroller_utils.h +++ b/src/mediacontroller/mediacontroller_utils.h @@ -59,6 +59,9 @@ extern const common::PlatformEnum MediaControllerDisplayR namespace utils { common::PlatformResult GetAllPlaylists(const std::string& app_id, picojson::array* playlists); common::ErrorCode ConvertMediaControllerError(int e); +bool isEncodable(std::string name); +common::PlatformResult EncodeMetadata(const picojson::object& to_encode, picojson::object* encoded); +common::PlatformResult DecodeMetadata(const picojson::value& to_decode, picojson::object* decoded); } // namespace utils } // namespace mediacontroller -- 2.34.1