From d4cd63129ae5dff8fd69525424e0f8cb9ae1a905 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Wed, 16 Feb 2022 21:54:47 +0100 Subject: Wip: fix video duration not working for some analyzed files, get frame in middle of video instead of first frame for thumbnail --- src/FileAnalyzer.cpp | 64 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 25 deletions(-) (limited to 'src/FileAnalyzer.cpp') diff --git a/src/FileAnalyzer.cpp b/src/FileAnalyzer.cpp index 6f1c1ee..4e4470e 100644 --- a/src/FileAnalyzer.cpp +++ b/src/FileAnalyzer.cpp @@ -21,7 +21,7 @@ namespace QuickMedia { // https://mimesniff.spec.whatwg.org/ // TODO: Test all of these - static const std::array magic_numbers = { + static const std::array magic_numbers = { MagicNumber{ {'R', 'I', 'F', 'F', -1, -1, -1, -1, 'A', 'V', 'I', ' '}, 12, ContentType::VIDEO_AVI }, MagicNumber{ {0x00, 0x00, 0x00, -1, 'f', 't', 'y', 'p', 'i', 's', 'o', 'm'}, 12, ContentType::VIDEO_MP4 }, MagicNumber{ {0x00, 0x00, 0x00, -1, 'f', 't', 'y', 'p', 'm', 'p', '4', '2'}, 12, ContentType::VIDEO_MP4 }, @@ -34,6 +34,7 @@ namespace QuickMedia { MagicNumber{ {0x00, 0x00, 0x01, 0xB3}, 4, ContentType::VIDEO_MPEG }, MagicNumber{ {0x1A, 0x45, 0xDF, 0xA3}, 4, ContentType::VIDEO_WEBM }, MagicNumber{ {'F', 'L', 'V', 0x01}, 4, ContentType::VIDEO_FLV }, + MagicNumber{ {0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF}, 7, ContentType::VIDEO_WMV }, MagicNumber{ {'.', 's', 'n', 'd'}, 4, ContentType::AUDIO_BASIC }, MagicNumber{ {'F', 'O', 'R', 'M', -1, -1, -1, -1, 'A', 'I', 'F', 'F'}, 12, ContentType::AUDIO_AIFF }, MagicNumber{ { 'I', 'D', '3' }, 3, ContentType::AUDIO_MPEG }, @@ -56,7 +57,7 @@ namespace QuickMedia { }; bool is_content_type_video(ContentType content_type) { - return content_type >= ContentType::VIDEO_AVI && content_type <= ContentType::VIDEO_FLV; + return content_type >= ContentType::VIDEO_AVI && content_type <= ContentType::VIDEO_WMV; } bool is_content_type_audio(ContentType content_type) { @@ -75,6 +76,7 @@ namespace QuickMedia { case ContentType::VIDEO_MPEG: return "video/mpeg"; case ContentType::VIDEO_WEBM: return "video/webm"; case ContentType::VIDEO_FLV: return "video/x-flv"; + case ContentType::VIDEO_WMV: return "video/x-ms-asf"; case ContentType::AUDIO_BASIC: return "audio/basic"; case ContentType::AUDIO_AIFF: return "audio/aiff"; case ContentType::AUDIO_MPEG: return "audio/mpeg"; @@ -106,6 +108,7 @@ namespace QuickMedia { || strcase_equals(ext, ".flv") || strcase_equals(ext, ".vob") || strcase_equals(ext, ".ogv") + || strcase_equals(ext, ".ogg") || strcase_equals(ext, ".avi") //|| strcase_equals(ext, ".ts") || strcase_equals(ext, ".mov") @@ -124,30 +127,37 @@ namespace QuickMedia { return 0; } - bool video_get_first_frame(const char *filepath, const char *destination_path, int width, int height) { + bool video_get_first_frame(const FileAnalyzer &file, const char *destination_path, int width, int height) { Path destination_path_tmp = destination_path; destination_path_tmp.append(".ftmp"); - const char *program_args[] = { "ffmpeg", "-y", "-v", "quiet", "-i", filepath, "-vframes", "1", "-f", "singlejpeg", "--", destination_path_tmp.data.c_str(), nullptr }; - std::string ffmpeg_result; - if(exec_program(program_args, nullptr, nullptr) != 0) { - fprintf(stderr, "Failed to execute ffmpeg, maybe its not installed?\n"); - return false; - } + const int middle_seconds = file.get_duration_seconds().value_or(0.0) / 2.0; + char middle_seconds_str[32]; + snprintf(middle_seconds_str, sizeof(middle_seconds_str), "%d", middle_seconds); if(width > 0 && height > 0) { - FileAnalyzer file_analyzer; - const bool success = file_analyzer.load_file(destination_path_tmp.data.c_str(), false) && create_thumbnail(destination_path_tmp, destination_path, mgl::vec2i(width, height), file_analyzer.get_content_type(), false); - remove(destination_path_tmp.data.c_str()); - return success; + char framesize[128]; + snprintf(framesize, sizeof(framesize), "%dx%d", width, height); + + const char *program_args[] = { "ffmpeg", "-y", "-v", "quiet", "-ss", middle_seconds_str, "-i", file.get_filepath().c_str(), "-vframes", "1", "-f", "singlejpeg", "-s", framesize, "--", destination_path_tmp.data.c_str(), nullptr }; + if(exec_program(program_args, nullptr, nullptr) != 0) { + fprintf(stderr, "Failed to execute ffmpeg, maybe its not installed?\n"); + return false; + } } else { - return rename_atomic(destination_path_tmp.data.c_str(), destination_path) == 0; + const char *program_args[] = { "ffmpeg", "-y", "-v", "quiet", "-ss", middle_seconds_str, "-i", file.get_filepath().c_str(), "-vframes", "1", "-f", "singlejpeg", "--", destination_path_tmp.data.c_str(), nullptr }; + if(exec_program(program_args, nullptr, nullptr) != 0) { + fprintf(stderr, "Failed to execute ffmpeg, maybe its not installed?\n"); + return false; + } } + + return rename_atomic(destination_path_tmp.data.c_str(), destination_path) == 0; } // TODO: Remove dependency on ffprobe static bool ffprobe_extract_metadata(const char *filepath, std::optional &dimensions, std::optional &duration_seconds) { - const char *program_args[] = { "ffprobe", "-v", "quiet", "-print_format", "json", "-show_streams", "--", filepath, nullptr }; + const char *program_args[] = { "ffprobe", "-v", "quiet", "-print_format", "json", "-show_streams", "-show_format", "--", filepath, nullptr }; std::string ffprobe_result; if(exec_program(program_args, accumulate_string, &ffprobe_result) != 0) { fprintf(stderr, "Failed to execute ffprobe, maybe its not installed?\n"); @@ -181,20 +191,19 @@ namespace QuickMedia { if(strcmp(codec_type.asCString(), "video") == 0) { const Json::Value &width_json = stream_json["width"]; const Json::Value &height_json = stream_json["height"]; - const Json::Value &duration_json = stream_json["duration"]; if(width_json.isNumeric() && height_json.isNumeric()) dimensions = { width_json.asInt(), height_json.asInt() }; - if(duration_json.isString()) - duration_seconds = atof(duration_json.asCString()); - break; - } else if(strcmp(codec_type.asCString(), "audio") == 0) { - const Json::Value &duration_json = stream_json["duration"]; - if(duration_json.isString()) - duration_seconds = atof(duration_json.asCString()); - // No break here because if there is video after this, we want it to overwrite this } } + const Json::Value &format_json = json_root["format"]; + if(!format_json.isObject()) + return true; + + const Json::Value &duration_json = format_json["duration"]; + if(duration_json.isString()) + duration_seconds = atof(duration_json.asCString()); + return true; } @@ -231,7 +240,7 @@ namespace QuickMedia { unsigned char magic_number_buffer[MAGIC_NUMBER_BUFFER_SIZE]; size_t num_bytes_read = fread(magic_number_buffer, 1, sizeof(magic_number_buffer), file); - if(feof(file)) { + if(feof(file) || num_bytes_read != sizeof(magic_number_buffer)) { perror(filepath); fclose(file); return false; @@ -263,10 +272,15 @@ namespace QuickMedia { duration_seconds = std::nullopt; } + this->filepath = filepath; loaded = true; return true; } + const std::string& FileAnalyzer::get_filepath() const { + return filepath; + } + ContentType FileAnalyzer::get_content_type() const { return content_type; } -- cgit v1.2.3