diff --git a/rpcs3/Emu/Cell/Modules/cellSearch.cpp b/rpcs3/Emu/Cell/Modules/cellSearch.cpp index 535474d760..173ad4a46b 100644 --- a/rpcs3/Emu/Cell/Modules/cellSearch.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSearch.cpp @@ -131,8 +131,8 @@ void populate_music_info(CellSearchMusicInfo& info, const utils::media_info& mi, info.releasedYear = static_cast(mi.get_metadata("date", -1)); info.duration = mi.duration_us / 1000; // we need microseconds info.samplingRate = mi.sample_rate; - info.bitrate = mi.bitrate_bps; - info.quantizationBitrate = mi.bitrate_bps; // TODO: Assumption, verify value + info.bitrate = mi.audio_bitrate_bps; + info.quantizationBitrate = mi.audio_bitrate_bps; // TODO: Assumption, verify value info.playCount = 0; // we do not track this for now info.lastPlayedDate = -1; // we do not track this for now info.importedDate = -1; // we do not track this for now @@ -140,7 +140,7 @@ void populate_music_info(CellSearchMusicInfo& info, const utils::media_info& mi, info.status = CELL_SEARCH_CONTENTSTATUS_AVAILABLE; // Convert AVCodecID to CellSearchCodec - switch (mi.av_codec_id) + switch (mi.audio_av_codec_id) { case 86017: // AV_CODEC_ID_MP3 info.codec = CELL_SEARCH_CODEC_MP3; @@ -182,6 +182,104 @@ void populate_music_info(CellSearchMusicInfo& info, const utils::media_info& mi, } } +void populate_video_info(CellSearchVideoInfo& info, const utils::media_info& mi, const fs::dir_entry& item) +{ + parse_metadata(info.albumTitle, mi, "album", "Unknown Album", CELL_SEARCH_TITLE_LEN_MAX); + parse_metadata(info.title, mi, "title", item.name.substr(0, item.name.find_last_of('.')), CELL_SEARCH_TITLE_LEN_MAX); + + info.size = item.size; + info.duration = mi.duration_us / 1000; // we need microseconds + info.audioBitrate = mi.audio_bitrate_bps; + info.videoBitrate = mi.video_bitrate_bps; + info.playCount = 0; // we do not track this for now + info.importedDate = -1; // we do not track this for now + info.takenDate = -1; // we do not track this for now + info.drmEncrypted = 0; // TODO: Needs to be 1 if it's encrypted + info.status = CELL_SEARCH_CONTENTSTATUS_AVAILABLE; + + // Convert Video AVCodecID to CellSearchCodec + switch (mi.video_av_codec_id) + { + case 1: // AV_CODEC_ID_MPEG1VIDEO + info.videoCodec = CELL_SEARCH_CODEC_MPEG1; + break; + case 2: // AV_CODEC_ID_MPEG2VIDEO + info.videoCodec = CELL_SEARCH_CODEC_MPEG2; + break; + case 12: // AV_CODEC_ID_MPEG4 + info.videoCodec = CELL_SEARCH_CODEC_MPEG4; + break; + case 27: // AV_CODEC_ID_H264 + info.videoCodec = CELL_SEARCH_CODEC_AVC; + break; + default: + info.videoCodec = CELL_SEARCH_CODEC_UNKNOWN; + info.status = CELL_SEARCH_CONTENTSTATUS_NOT_SUPPORTED; + break; + } + + // Convert Audio AVCodecID to CellSearchCodec + switch (mi.audio_av_codec_id) + { + // Let's ignore this due to CELL_SEARCH_CODEC_MPEG1_LAYER3 + //case 86017: // AV_CODEC_ID_MP3 + // info.audioCodec = CELL_SEARCH_CODEC_MP3; + // break; + case 86018: // AV_CODEC_ID_AAC + info.audioCodec = CELL_SEARCH_CODEC_AAC; + break; + case 86019: // AV_CODEC_ID_AC3 + info.audioCodec = CELL_SEARCH_CODEC_AC3; + break; + case 86023: // AV_CODEC_ID_WMAV1 + case 86024: // AV_CODEC_ID_WMAV2 + info.audioCodec = CELL_SEARCH_CODEC_WMA; + break; + case 86047: // AV_CODEC_ID_ATRAC3 + info.audioCodec = CELL_SEARCH_CODEC_AT3; + break; + case 86055: // AV_CODEC_ID_ATRAC3P + info.audioCodec = CELL_SEARCH_CODEC_AT3P; + break; + case 88078: // AV_CODEC_ID_ATRAC3AL + //case 88079: // AV_CODEC_ID_ATRAC3PAL TODO: supported ? + info.audioCodec = CELL_SEARCH_CODEC_ATALL; + break; + // TODO: Find out if any of this works + //case 88069: // AV_CODEC_ID_DSD_LSBF + //case 88070: // AV_CODEC_ID_DSD_MSBF + //case 88071: // AV_CODEC_ID_DSD_LSBF_PLANAR + //case 88072: // AV_CODEC_ID_DSD_MSBF_PLANAR + // info.audioCodec = CELL_SEARCH_CODEC_DSD; + // break; + //case ???: + // info.audioCodec = CELL_SEARCH_CODEC_WAV; + // break; + case 86058: // AV_CODEC_ID_MP1 + info.audioCodec = CELL_SEARCH_CODEC_MPEG1_LAYER1; + break; + case 86016: // AV_CODEC_ID_MP2 + info.audioCodec = CELL_SEARCH_CODEC_MPEG1_LAYER2; + break; + case 86017: // AV_CODEC_ID_MP3 + info.audioCodec = CELL_SEARCH_CODEC_MPEG1_LAYER3; + break; + //case ???: + // info.audioCodec = CELL_SEARCH_CODEC_MPEG2_LAYER1; + // break; + //case ???: + // info.audioCodec = CELL_SEARCH_CODEC_MPEG2_LAYER2; + // break; + //case ???: + // info.audioCodec = CELL_SEARCH_CODEC_MPEG2_LAYER3; + // break; + default: + info.audioCodec = CELL_SEARCH_CODEC_UNKNOWN; + info.status = CELL_SEARCH_CONTENTSTATUS_NOT_SUPPORTED; + break; + } +} + error_code cellSearchInitialize(CellSearchMode mode, u32 container, vm::ptr func, vm::ptr userData) { cellSearch.warning("cellSearchInitialize(mode=0x%x, container=0x%x, func=*0x%x, userData=*0x%x)", +mode, container, func, userData); @@ -681,21 +779,15 @@ error_code cellSearchStartContentSearchInList(vm::cptr list else if (type == CELL_SEARCH_CONTENTSEARCHTYPE_VIDEO_ALL) { curr_find->type = CELL_SEARCH_CONTENTTYPE_VIDEO; - CellSearchVideoInfo& info = curr_find->data.video; - // TODO - Some kinda file video analysis and assign the values as such - info.duration = 0; - info.size = item.size; - info.importedDate = 0; - info.takenDate = 0; - info.videoBitrate = 0; - info.audioBitrate = 0; - info.playCount = 0; - info.drmEncrypted = 0; - info.videoCodec = 0; // CellSearchCodec - info.audioCodec = 0; // CellSearchCodec - info.status = 0; // CellSearchContentStatus - strcpy_trunc(info.title, item.name.substr(0, ext_offset)); // it'll do for the moment... - strcpy_trunc(info.albumTitle, "ALBUM TITLE"); + + const std::string path = vfs::get(vpath) + "/" + item.name; + const auto [success, mi] = utils::get_media_info(path, 0); // AVMEDIA_TYPE_VIDEO + if (!success) + { + continue; + } + + populate_video_info(curr_find->data.video, mi, item); } content_map.map.emplace(hash, curr_find); @@ -862,34 +954,28 @@ error_code cellSearchStartContentSearch(CellSearchContentSearchType type, CellSe CellSearchPhotoInfo& info = curr_find->data.photo; // TODO - Some kinda file photo analysis and assign the values as such info.size = item.size; - info.importedDate = 0; - info.takenDate = 0; + info.importedDate = -1; + info.takenDate = -1; info.width = 0; info.height = 0; - info.orientation = 0; // CellSearchOrientation - info.codec = 0; // CellSearchCodec - info.status = 0; // CellSearchContentStatus + info.orientation = CELL_SEARCH_ORIENTATION_UNKNOWN; + info.codec = CELL_SEARCH_CODEC_UNKNOWN; + info.status = CELL_SEARCH_CONTENTSTATUS_AVAILABLE; strcpy_trunc(info.title, item.name.substr(0, ext_offset)); strcpy_trunc(info.albumTitle, "ALBUM TITLE"); } else if (type == CELL_SEARCH_CONTENTSEARCHTYPE_VIDEO_ALL) { curr_find->type = CELL_SEARCH_CONTENTTYPE_VIDEO; - CellSearchVideoInfo& info = curr_find->data.video; - // TODO - Some kinda file video analysis and assign the values as such - info.duration = 0; - info.size = item.size; - info.importedDate = 0; - info.takenDate = 0; - info.videoBitrate = 0; - info.audioBitrate = 0; - info.playCount = 0; - info.drmEncrypted = 0; - info.videoCodec = 0; // CellSearchCodec - info.audioCodec = 0; // CellSearchCodec - info.status = 0; // CellSearchContentStatus - strcpy_trunc(info.title, item.name.substr(0, ext_offset)); // it'll do for the moment... - strcpy_trunc(info.albumTitle, "ALBUM TITLE"); + + const std::string path = vfs::get(vpath) + "/" + item.name; + const auto [success, mi] = utils::get_media_info(path, 0); // AVMEDIA_TYPE_VIDEO + if (!success) + { + continue; + } + + populate_video_info(curr_find->data.video, mi, item); } content_map.map.emplace(hash, curr_find); diff --git a/rpcs3/util/media_utils.cpp b/rpcs3/util/media_utils.cpp index 7f54621c72..71803efc02 100644 --- a/rpcs3/util/media_utils.cpp +++ b/rpcs3/util/media_utils.cpp @@ -80,40 +80,64 @@ namespace utils if (int err = avformat_find_stream_info(av_format_ctx, nullptr); err < 0) { // Failed to load stream information + avformat_close_input(&av_format_ctx); avformat_free_context(av_format_ctx); media_log.notice("avformat_find_stream_info: could not load stream information. error=%d file='%s'", err, path); return { false, std::move(info) }; } - // Derive first stream id and type from avformat context - // we are only interested in the first stream for now - int stream_index = -1; - for (uint i = 0; i < 1 && i < av_format_ctx->nb_streams; i++) + // Derive first stream id and type from avformat context. + // We are only interested in the first matching stream for now. + int audio_stream_index = -1; + int video_stream_index = -1; + for (uint i = 0; i < av_format_ctx->nb_streams; i++) { - if (av_format_ctx->streams[i]->codecpar->codec_type == av_media_type) + switch (av_format_ctx->streams[i]->codecpar->codec_type) { - stream_index = i; + case AVMEDIA_TYPE_AUDIO: + if (audio_stream_index < 0) + audio_stream_index = i; + break; + case AVMEDIA_TYPE_VIDEO: + if (video_stream_index < 0) + video_stream_index = i; + break; + default: break; } } - if (stream_index == -1) + // Abort if there is no natching stream or if the stream isn't the first one + if (av_media_type == AVMEDIA_TYPE_AUDIO && audio_stream_index != 0 || + av_media_type == AVMEDIA_TYPE_VIDEO && video_stream_index != 0) { // Failed to find a stream + avformat_close_input(&av_format_ctx); avformat_free_context(av_format_ctx); - media_log.notice("Could not find the desired stream of type %d in file='%s'", av_media_type, path); + media_log.notice("Failed to match stream of type %d in file='%s'", av_media_type, path); return { false, std::move(info) }; } - AVStream* stream = av_format_ctx->streams[stream_index]; - AVCodecParameters* codec = stream->codecpar; - AVDictionaryEntry* tag = nullptr; + // Get video info if available + if (video_stream_index >= 0) + { + const AVStream* stream = av_format_ctx->streams[video_stream_index]; + info.video_av_codec_id = stream->codecpar->codec_id; + info.video_bitrate_bps = stream->codecpar->bit_rate; + } + + // Get audio info if available + if (audio_stream_index >= 0) + { + const AVStream* stream = av_format_ctx->streams[audio_stream_index]; + info.audio_av_codec_id = stream->codecpar->codec_id; + info.audio_bitrate_bps = stream->codecpar->bit_rate; + info.sample_rate = stream->codecpar->sample_rate; + } - info.av_codec_id = codec->codec_id; - info.bitrate_bps = codec->bit_rate; - info.sample_rate = codec->sample_rate; info.duration_us = av_format_ctx->duration; + AVDictionaryEntry* tag = nullptr; while (tag = av_dict_get(av_format_ctx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX)) { info.metadata[tag->key] = tag->value; diff --git a/rpcs3/util/media_utils.h b/rpcs3/util/media_utils.h index cf1c63d1c2..2e95397398 100644 --- a/rpcs3/util/media_utils.h +++ b/rpcs3/util/media_utils.h @@ -6,9 +6,11 @@ namespace utils { struct media_info { - s32 av_codec_id = 0; // 0 = AV_CODEC_ID_NONE - s32 bitrate_bps = 0; // Bit rate in bit/s - s32 sample_rate; // Samples per second + s32 audio_av_codec_id = 0; // 0 = AV_CODEC_ID_NONE + s32 video_av_codec_id = 0; // 0 = AV_CODEC_ID_NONE + s32 audio_bitrate_bps = 0; // Bit rate in bit/s + s32 video_bitrate_bps = 0; // Bit rate in bit/s + s32 sample_rate = 0; // Samples per second s64 duration_us = 0; // in AV_TIME_BASE fractional seconds (= microseconds) std::unordered_map metadata;