Сохранить фрагмент из видеофайла (нарезка на фрагменты)

Ответить
ya
^-^
Сообщения: 2994
Зарегистрирован: 16 дек 2021, 19:56

Сохранить фрагмент из видеофайла (нарезка на фрагменты)

Сообщение ya »

Компиляция: Использование:

Код: Выделить всё

splitter видеофайл.mp4 0:0:59 0:1:37
где 0:0:59 - начало фрагмента, а 0:1:37 - конец фрагмента, причём конец фрагмента может отсутствовать
Вложения
splitter.zip
(676.35 КБ) 238 скачиваний
splitter.zip
(676.35 КБ) 238 скачиваний
ya
^-^
Сообщения: 2994
Зарегистрирован: 16 дек 2021, 19:56

Re: Сохранить фрагмент из видеофайла (нарезка на фрагменты)

Сообщение ya »

добавлено: если ранее создан файл с метками времени, то не создавать его заново

splitter.cpp

Код: Выделить всё

#include <iostream>
#include <iomanip>
#include <sstream>
#include <vector>
#include <string>
#include <fstream>
#include <cstdlib> // Для system()
#include <algorithm>
#include <ctime>   // Для работы с датой и временем

const std::string OUTPUT_SUFFIX = ".output.mp4"; // Суффикс для выходного файла

struct Time {
    int hours, minutes;
    double seconds;

    Time(int h = 0, int m = 0, double s = 0.0) : hours(h), minutes(m), seconds(s) {}

    std::string toString() const {
        std::stringstream ss;
        ss << std::setw(2) << std::setfill('0') << hours << ":"
           << std::setw(2) << std::setfill('0') << minutes << ":"
           << std::fixed << std::setprecision(6) << std::setfill('0') << seconds;
        return ss.str();
    }

    bool operator<(const Time& other) const {
        if (hours != other.hours) return hours < other.hours;
        if (minutes != other.minutes) return minutes < other.minutes;
        return seconds < other.seconds;
    }

    bool operator>(const Time& other) const {
        if (hours != other.hours) return hours > other.hours;
        if (minutes != other.minutes) return minutes > other.minutes;
        return seconds > other.seconds;
    }

    void subtract(double sec) {
        seconds -= sec;

        if (seconds < 0) {
            seconds += 60;
            minutes--;
        }

        if (minutes < 0) {
            minutes += 60;
            hours--;
        }

        if (hours < 0) {
            hours = 0;
            minutes = 0;
            seconds = 0.0;
        }
    }
};

bool runFFProbe(const std::string& input_file, const std::string& output_file) {
    std::string command = "ffprobe -i \"" + input_file + "\" -select_streams v -skip_frame nokey -show_frames -show_entries frame=pkt_pts_time -sexagesimal -print_format csv > \"" + output_file + "\"";
    return system(command.c_str()) == 0; // Возвращает true, если команда выполнена успешно
}

bool checkIfFileExists(const std::string& filename) {
    std::ifstream file(filename);
    return file.good();
}

void formatTimestamps(const std::string& input_file, const std::string& output_file) {
    std::ifstream infile(input_file);
    std::ofstream outfile(output_file);
    std::string line;
    
    while (std::getline(infile, line)) {
        size_t comma_pos = line.find(',');
        if (comma_pos != std::string::npos) {
            // Печатаем значение времени во втором столбце
            outfile << line.substr(comma_pos + 1) << std::endl;
        }
    }
}

std::vector<Time> readKeyframesFromFile(const std::string& filename) {
    std::ifstream file(filename);
    std::vector<Time> timestamps;
    std::string line;

    while (std::getline(file, line)) {
        std::istringstream ss(line);
        int h, m;
        double s;

        char colon1, colon2;
        ss >> h >> colon1 >> m >> colon2 >> s;

        if (ss && colon1 == ':' && colon2 == ':') {
            timestamps.emplace_back(h, m, s);
        }
    }

    return timestamps;
}

Time findMaxTimeLessThan(const std::string& filename, const Time& user_time) {
    std::ifstream file(filename);
    std::string line;
    Time max_time(0, 0, 0.0);

    while (std::getline(file, line)) {
        std::istringstream ss(line);
        int h, m;
        double s;

        char colon1, colon2;
        ss >> h >> colon1 >> m >> colon2 >> s;

        if (ss && colon1 == ':' && colon2 == ':') {
            Time current_time(h, m, s);
            if (current_time < user_time && current_time > max_time) {
                max_time = current_time;
            }
        }
    }
    return max_time;
}

// Функция для получения текущей даты и времени в нужном формате
std::string getCurrentDateTimeString() {
    std::time_t now = std::time(nullptr);
    std::tm* local_time = std::localtime(&now);
    
    std::ostringstream oss;
    oss << std::put_time(local_time, "%Y.%m.%d_%H-%M-%S");
    
    return oss.str();
}

// Функция для извлечения расширения файла
std::string getFileExtension(const std::string& filename) {
    size_t dot_pos = filename.rfind('.');
    if (dot_pos != std::string::npos) {
        return filename.substr(dot_pos); // Возвращает все, что после последней точки
    }
    return ""; // Если точка не найдена, возвращаем пустую строку
}

int main(int argc, char* argv[]) {
    if (argc < 3) {
        std::cerr << "Usage: " << argv[0] << " <input_file> <time_to_compare> <end_time (optional)>" << std::endl;
        return 1;
    }

    std::string input_file = argv[1];
    std::string search_time_str = argv[2];
    std::string end_time_str = (argc > 3) ? argv[3] : ""; // Опциональный третий аргумент

    // Генерация выходного файла ключевых кадров
    std::string keyframe_file = input_file + "_keyframes.txt";
	if (!checkIfFileExists(keyframe_file))
	{
    // Запуск ffprobe для извлечения ключевых кадров
    if (!runFFProbe(input_file, keyframe_file)) {
        std::cerr << "Error running ffprobe." << std::endl;
        return 1;
    }

    // Создание файла с отформатированными временными метками
    std::string formatted_keyframe_file = input_file + "_formatted_keyframes.txt";
    formatTimestamps(keyframe_file, formatted_keyframe_file);
    
    // Удаление оригинального файла с ключевыми кадрами
    remove(keyframe_file.c_str());
    
    // Переименование отформатированного файла
    rename(formatted_keyframe_file.c_str(), keyframe_file.c_str());
	}
    // Разбор введенного времени пользователем
    std::istringstream ss(search_time_str);
    int h, m;
    double s;
    char colon1, colon2;
    ss >> h >> colon1 >> m >> colon2 >> s;

    Time user_time(h, m, s);

    // Поиск наибольшего значения временной метки, меньшего чем user_time
    Time max_time = findMaxTimeLessThan(keyframe_file, user_time);

    // Вывод результата
    if (max_time.seconds > 0) {
        std::cout << "The largest time less than " << user_time.toString() << " is " << max_time.toString() << std::endl;
        
        max_time.subtract(0.1); // Вычитаем 0.1 секунды
        std::cout << "The time after subtracting 0:0:0.1 is " << max_time.toString() << std::endl;

        // Формирование команды ffmpeg
//        std::string current_datetime_suffix = getCurrentDateTimeString();
//        std::string output_file = input_file + "." + current_datetime_suffix + ".mp4"; 

		// Извлечение расширения входного файла
        std::string file_extension = getFileExtension(input_file);
        std::string current_datetime_suffix = getCurrentDateTimeString();
        
        // Создание имени выходного файла с учетом расширения
        std::string output_file = input_file.substr(0, input_file.size() - file_extension.size()) + "." + current_datetime_suffix + file_extension;
        
        // Формирование команды ffmpeg
        std::string command = "ffmpeg -i \"" + input_file + "\" -map_metadata -1 -map_chapters -1 -avoid_negative_ts make_zero -ss " +
                              max_time.toString() + (end_time_str.empty() ? " " : (" -to " + end_time_str) ) +
                              " -c copy \"" + output_file + "\"";

        // Выполнение команды ffmpeg
        if (system(command.c_str()) != 0) {
            std::cerr << "Error running ffmpeg." << std::endl;
            return 1;
        }
    } else {
        std::cout << "No time found less than " << user_time.toString() << "." << std::endl;
    }

    return 0;
}
Makefile

Код: Выделить всё

# Определяем компилятор и флаги компиляции
CXX = g++
CXXFLAGS = -std=c++11 -Wall -Wextra

# Основной целевой файл
TARGET = splitter

# Исходные файлы
SRCS = splitter.cpp  # Замените 'main.cpp' на имя вашего файла с кодом

# Объектные файлы
OBJS = $(SRCS:.cpp=.o)

# Правило по умолчанию для сборки
all: $(TARGET)

# Линковка объекта в исполняемый файл
$(TARGET): $(OBJS)
	$(CXX) $(OBJS) -o $(TARGET)

# Правило для компиляции исходников в объектные файлы
%.o: %.cpp
	$(CXX) $(CXXFLAGS) -c $< -o $@

# Правило для очистки
clean:
	rm -f $(OBJS) $(TARGET)

.PHONY: all clean
Вложения
splitter.zip
исходники и откомпилированный экзэшник
(669.41 КБ) 208 скачиваний
splitter.zip
исходники и откомпилированный экзэшник
(669.41 КБ) 208 скачиваний
ya
^-^
Сообщения: 2994
Зарегистрирован: 16 дек 2021, 19:56

Re: Сохранить фрагмент из видеофайла (нарезка на фрагменты)

Сообщение ya »

Внесённые изменения: рефакторинг кода

splitter1.cpp

Код: Выделить всё

#include <iostream>
#include <iomanip>
#include <sstream>
#include <vector>
#include <string>
#include <fstream>
#include <cstdlib> // Для system()
#include <algorithm>
#include <ctime>   // Для работы с датой и временем

class Time {
private:
    int hours, minutes;
    double seconds;

public:
    Time(int h = 0, int m = 0, double s = 0.0) : hours(h), minutes(m), seconds(s) {}

    std::string toString() const {
        std::stringstream ss;
        ss << std::setw(2) << std::setfill('0') << hours << ":"
           << std::setw(2) << std::setfill('0') << minutes << ":"
           << std::fixed << std::setprecision(6) << std::setfill('0') << seconds;
        return ss.str();
    }

    bool operator<(const Time& other) const {
        return std::tie(hours, minutes, seconds) < std::tie(other.hours, other.minutes, other.seconds);
    }

    bool operator>(const Time& other) const {
        return std::tie(hours, minutes, seconds) > std::tie(other.hours, other.minutes, other.seconds);
    }

    void subtract(double sec) {
        seconds -= sec;
        while (seconds < 0) {
            seconds += 60;
            minutes--;
        }
        while (minutes < 0) {
            minutes += 60;
            hours--;
        }
        if (hours < 0) {
            hours = minutes = 0;
            seconds = 0.0;
        }
    }

    // Добавим метод для проверки валидности времени
    bool isValid() const {
        return (hours >= 0 && minutes >= 0 && seconds >= 0);
    }

    // Метод для доступа к полю seconds
    double getSeconds() const {
        return seconds;
    }
};

class FrameExtractor {
public:
    static bool runFFProbe(const std::string& input_file, const std::string& output_file) {
        std::string command = "ffprobe -i \"" + input_file + "\" -select_streams v -skip_frame nokey -show_frames " 
                              "-show_entries frame=pkt_pts_time -sexagesimal -print_format csv > \"" + output_file + "\"";
        return system(command.c_str()) == 0; // Возвращает true, если команда выполнена успешно
    }

    static void formatTimestamps(const std::string& input_file, const std::string& output_file) {
        std::ifstream infile(input_file);
        std::ofstream outfile(output_file);
        std::string line;

        while (std::getline(infile, line)) {
            size_t comma_pos = line.find(',');
            if (comma_pos != std::string::npos) {
                outfile << line.substr(comma_pos + 1) << std::endl; // Печатаем значение времени во втором столбце
            }
        }
    }
};

class TimeUtils {
public:
    static std::string getCurrentDateTimeString() {
        std::time_t now = std::time(nullptr);
        std::tm* local_time = std::localtime(&now);
        std::ostringstream oss;
        oss << std::put_time(local_time, "%Y.%m.%d_%H-%M-%S");
        return oss.str();
    }

    static std::string getFileExtension(const std::string& filename) {
        size_t dot_pos = filename.rfind('.');
        return (dot_pos != std::string::npos) ? filename.substr(dot_pos) : ""; // Возвращает все, что после последней точки
    }
};

class TimestampFileParser {
public:
    static std::vector<Time> readKeyframesFromFile(const std::string& filename) {
        std::ifstream file(filename);
        std::vector<Time> timestamps;
        std::string line;
        while (std::getline(file, line)) {
            Time time = parseTime(line);
            if (time.isValid()) {
                timestamps.push_back(time);
            }
        }
        return timestamps;
    }

    static Time findMaxTimeLessThan(const std::vector<Time>& timestamps, const Time& user_time) {
        Time max_time;
        for (const auto& current_time : timestamps) {
            if (current_time < user_time && current_time > max_time) {
                max_time = current_time;
            }
        }
        return max_time;
    }

private:
    static Time parseTime(const std::string& time_string) {
        int h, m;
        double s;
        char colon1, colon2;
        std::istringstream ss(time_string);
        ss >> h >> colon1 >> m >> colon2 >> s;

        if (ss && colon1 == ':' && colon2 == ':') {
            return Time(h, m, s);
        }
        return Time(); // Возвращаем время по умолчанию (0, 0, 0)
    }
};

class Application {
private:
    std::string input_file;
    std::string keyframe_file;

public:
    Application(const std::string& file) : input_file(file) {
        keyframe_file = input_file + "_keyframes.txt";
    }

    void process(const std::string& search_time_str, const std::string& end_time_str) {
        if (!checkIfFileExists(keyframe_file)) {
            extractKeyframes();
        }

        Time user_time = parseSearchTime(search_time_str);
        auto timestamps = TimestampFileParser::readKeyframesFromFile(keyframe_file);
        Time max_time = TimestampFileParser::findMaxTimeLessThan(timestamps, user_time);

        if (max_time.getSeconds() > 0) { // Используем метод getSeconds()
            handleFoundTime(max_time, end_time_str);
        } else {
            std::cout << "No time found less than " << user_time.toString() << "." << std::endl;
        }
    }

private:
    void extractKeyframes() {
        std::string formatted_keyframe_file = input_file + "_formatted_keyframes.txt";
        if (!FrameExtractor::runFFProbe(input_file, keyframe_file)) {
            throw std::runtime_error("Error running ffprobe.");
        }
        FrameExtractor::formatTimestamps(keyframe_file, formatted_keyframe_file);
        // Удаляем оригинальный файл с ключевыми кадрами
        remove(keyframe_file.c_str());
        // Переименовываем отформатированный файл
        rename(formatted_keyframe_file.c_str(), keyframe_file.c_str());
    }

    Time parseSearchTime(const std::string& search_time_str) {
        int h, m;
        double s;
        char colon1, colon2;
        std::istringstream ss(search_time_str);
        ss >> h >> colon1 >> m >> colon2 >> s;
        return Time(h, m, s);
    }

    void handleFoundTime(Time& max_time, const std::string& end_time_str) {
        max_time.subtract(0.1); // Вычитаем 0.1 секунды
        std::string output_file = generateOutputFilename();
        std::string command = buildFFmpegCommand(max_time, end_time_str, output_file);
        
        if (system(command.c_str()) != 0) {
            throw std::runtime_error("Error running ffmpeg.");
        }

        std::cout << "The largest time less than " << max_time.toString() 
                  << " is " << max_time.toString() << std::endl;
    }

    std::string generateOutputFilename() {
        std::string file_extension = TimeUtils::getFileExtension(input_file);
        std::string current_datetime_suffix = TimeUtils::getCurrentDateTimeString();
        return input_file.substr(0, input_file.size() - file_extension.size()) 
               + "." + current_datetime_suffix + file_extension;
    }

    std::string buildFFmpegCommand(const Time& max_time, const std::string& end_time_str, const std::string& output_file) {
        return "ffmpeg -i \"" + input_file + "\" -map_metadata -1 -map_chapters -1 -avoid_negative_ts make_zero -ss "
               + max_time.toString() + (end_time_str.empty() ? "" : " -to " + end_time_str) + " -c copy \"" + output_file + "\"";
    }

    bool checkIfFileExists(const std::string& filename) {
        std::ifstream file(filename);
        return file.good();
    }
};

int main(int argc, char* argv[]) {
    if (argc < 3) {
        std::cerr << "Usage: " << argv[0] << " <input_file> <time_to_compare> <end_time (optional)>" << std::endl;
        return 1;
    }

    try {
        std::string input_file = argv[1];
        std::string search_time_str = argv[2];
        std::string end_time_str = (argc > 3) ? argv[3] : ""; // Опциональный третий аргумент

        Application app(input_file);
        app.process(search_time_str, end_time_str);
    } catch (const std::runtime_error& e) {
        std::cerr << e.what() << std::endl;
        return 1;
    }

    return 0;
}

Вложения
ffprobe.zip
(16.15 МБ) 222 скачивания
ffprobe.zip
(16.15 МБ) 222 скачивания
splitter.zip
(694.27 КБ) 179 скачиваний
splitter.zip
(694.27 КБ) 179 скачиваний
ya
^-^
Сообщения: 2994
Зарегистрирован: 16 дек 2021, 19:56

Re: Сохранить фрагмент из видеофайла (нарезка на фрагменты)

Сообщение ya »

исправлено: условие при котором время начала может быть равно 0

splitter3.cpp

Код: Выделить всё

#include <iostream>
#include <iomanip>
#include <sstream>
#include <vector>
#include <string>
#include <fstream>
#include <cstdlib> // Для system()
#include <algorithm>
#include <ctime>   // Для работы с датой и временем
#include <tuple>

class Time {
private:
    int hours, minutes;
    double seconds;

public:
    Time(int h = 0, int m = 0, double s = 0.0) : hours(h), minutes(m), seconds(s) {}

    std::string toString() const {
        std::stringstream ss;
        ss << std::setw(2) << std::setfill('0') << hours << ":"
           << std::setw(2) << std::setfill('0') << minutes << ":"
           << std::fixed << std::setprecision(6) << std::setfill('0') << seconds;
        return ss.str();
    }

    bool operator<(const Time& other) const {
        return std::tie(hours, minutes, seconds) < std::tie(other.hours, other.minutes, other.seconds);
    }

    bool operator>(const Time& other) const {
        return std::tie(hours, minutes, seconds) > std::tie(other.hours, other.minutes, other.seconds);
    }

    void subtract(double sec) {
        seconds -= sec;
        while (seconds < 0) {
            seconds += 60;
            minutes--;
        }
        while (minutes < 0) {
            minutes += 60;
            hours--;
        }
        if (hours < 0) {
            hours = minutes = 0;
            seconds = 0.0;
        }
    }

    // Добавим метод для проверки валидности времени
    bool isValid() const {
        return (hours >= 0 && minutes >= 0 && seconds >= 0);
    }

    // Метод для доступа к полю seconds
    double getSeconds() const {
        return seconds;
    }
};

class FrameExtractor {
public:
    static bool runFFProbe(const std::string& input_file, const std::string& output_file) {
        std::string command = "ffprobe -i \"" + input_file + "\" -select_streams v -skip_frame nokey -show_frames " 
                              "-show_entries frame=pkt_pts_time -sexagesimal -print_format csv > \"" + output_file + "\"";
        return system(command.c_str()) == 0; // Возвращает true, если команда выполнена успешно
    }

    static void formatTimestamps(const std::string& input_file, const std::string& output_file) {
        std::ifstream infile(input_file);
        std::ofstream outfile(output_file);
        std::string line;

        while (std::getline(infile, line)) {
            size_t comma_pos = line.find(',');
            if (comma_pos != std::string::npos) {
                outfile << line.substr(comma_pos + 1) << std::endl; // Печатаем значение времени во втором столбце
            }
        }
    }
};

class TimeUtils {
public:
    static std::string getCurrentDateTimeString() {
        std::time_t now = std::time(nullptr);
        std::tm* local_time = std::localtime(&now);
        std::ostringstream oss;
        oss << std::put_time(local_time, "%Y.%m.%d_%H-%M-%S");
        return oss.str();
    }

    static std::string getFileExtension(const std::string& filename) {
        size_t dot_pos = filename.rfind('.');
        return (dot_pos != std::string::npos) ? filename.substr(dot_pos) : ""; // Возвращает все, что после последней точки
    }
};

class TimestampFileParser {
public:
    static std::vector<Time> readKeyframesFromFile(const std::string& filename) {
        std::ifstream file(filename);
        std::vector<Time> timestamps;
        std::string line;
        while (std::getline(file, line)) {
            Time time = parseTime(line);
            if (time.isValid()) {
                timestamps.push_back(time);
            }
        }
        return timestamps;
    }

    static Time findMaxTimeLessThan(const std::vector<Time>& timestamps, const Time& user_time) {
        Time max_time;
        for (const auto& current_time : timestamps) {
            if (current_time < user_time && current_time > max_time) {
                max_time = current_time;
            }
        }
        return max_time;
    }

private:
    static Time parseTime(const std::string& time_string) {
        int h, m;
        double s;
        char colon1, colon2;
        std::istringstream ss(time_string);
        ss >> h >> colon1 >> m >> colon2 >> s;

        if (ss && colon1 == ':' && colon2 == ':') {
            return Time(h, m, s);
        }
        return Time(); // Возвращаем время по умолчанию (0, 0, 0)
    }
};


class Application {
private:
    std::string input_file;
    std::string keyframe_file;

public:
    Application(const std::string& file) : input_file(file) {
        keyframe_file = input_file + "_keyframes.txt";
    }

    void process(const std::string& search_time_str, const std::string& end_time_str) {
        if (search_time_str == "0") {
            // Если second argument is "0", просто вызываем ffmpeg без дополнительных операций
            generateAndRunFFmpegCommand(end_time_str);
            return; // Возвращаемся, чтобы не производить дальнейшие операции
        }

        if (!checkIfFileExists(keyframe_file)) {
            extractKeyframes();
        }

        Time user_time = parseSearchTime(search_time_str);
        auto timestamps = TimestampFileParser::readKeyframesFromFile(keyframe_file);
        Time max_time = TimestampFileParser::findMaxTimeLessThan(timestamps, user_time);

        if (max_time.getSeconds() > 0) {
            handleFoundTime(max_time, end_time_str);
        } else {
            std::cout << "No time found less than " << user_time.toString() << "." << std::endl;
        }
    }

private:
    void extractKeyframes() {
        std::string formatted_keyframe_file = input_file + "_formatted_keyframes.txt";
        if (!FrameExtractor::runFFProbe(input_file, keyframe_file)) {
            throw std::runtime_error("Error running ffprobe.");
        }
        FrameExtractor::formatTimestamps(keyframe_file, formatted_keyframe_file);
        remove(keyframe_file.c_str());
        rename(formatted_keyframe_file.c_str(), keyframe_file.c_str());
    }

    Time parseSearchTime(const std::string& search_time_str) {
        int h, m;
        double s;
        char colon1, colon2;
        std::istringstream ss(search_time_str);
        ss >> h >> colon1 >> m >> colon2 >> s;
        return Time(h, m, s);
    }

    void handleFoundTime(Time& max_time, const std::string& end_time_str) {
        max_time.subtract(0.1);
        std::string output_file = generateOutputFilename();
        std::string command = buildFFmpegCommand(max_time, end_time_str, output_file);
        
        if (system(command.c_str()) != 0) {
            throw std::runtime_error("Error running ffmpeg.");
        }

        std::cout << "The largest time less than " << max_time.toString() 
                  << " is " << max_time.toString() << std::endl;
    }

    void generateAndRunFFmpegCommand(const std::string& end_time_str) {
        std::string output_file = generateOutputFilename();
        std::string command = "ffmpeg -i \"" + input_file + "\" -map_metadata -1 -map_chapters -1 -avoid_negative_ts make_zero -c copy \"" + output_file + "\"";

        if (!end_time_str.empty()) {
            command.insert(command.find("-c copy") - 1, " -to " + end_time_str); // Добавление параметра -to
        }

        if (system(command.c_str()) != 0) {
            throw std::runtime_error("Error running ffmpeg.");
        }

        std::cout << "Command executed successfully: " << command << std::endl;
    }

    std::string generateOutputFilename() {
        std::string file_extension = TimeUtils::getFileExtension(input_file);
        std::string current_datetime_suffix = TimeUtils::getCurrentDateTimeString();
        return input_file.substr(0, input_file.size() - file_extension.size()) 
               + "." + current_datetime_suffix + file_extension;
    }

    std::string buildFFmpegCommand(const Time& max_time, const std::string& end_time_str, const std::string& output_file) {
        return "ffmpeg -i \"" + input_file + "\" -map_metadata -1 -map_chapters -1 -avoid_negative_ts make_zero -ss "
               + max_time.toString() + (end_time_str.empty() ? "" : " -to " + end_time_str) + " -c copy \"" + output_file + "\"";
    }

    bool checkIfFileExists(const std::string& filename) {
        std::ifstream file(filename);
        return file.good();
    }
};

int main(int argc, char* argv[]) {
    if (argc < 3) {
        std::cerr << "Usage: " << argv[0] << " <input_file> <time_to_compare> <end_time (optional)>" << std::endl;
        return 1;
    }

    try {
        std::string input_file = argv[1];
        std::string search_time_str = argv[2];
        std::string end_time_str = (argc > 3) ? argv[3] : "";

        Application app(input_file);
        app.process(search_time_str, end_time_str);
    } catch (const std::runtime_error& e) {
        std::cerr << e.what() << std::endl;
        return 1;
    }

    return 0;
}
Makefile

Код: Выделить всё

# Определяем компилятор и флаги компиляции
CXX = g++
CXXFLAGS = -std=c++11 -Wall -Wextra

# Основной целевой файл
TARGET = splitter

# Исходные файлы
SRCS = splitter3.cpp  # Замените 'main.cpp' на имя вашего файла с кодом

# Объектные файлы
OBJS = $(SRCS:.cpp=.o)

# Правило по умолчанию для сборки
all: $(TARGET)

# Линковка объекта в исполняемый файл
$(TARGET): $(OBJS)
	$(CXX) $(OBJS) -o $(TARGET)

# Правило для компиляции исходников в объектные файлы
%.o: %.cpp
	$(CXX) $(CXXFLAGS) -c $< -o $@

# Правило для очистки
clean:
	rm -f $(OBJS) $(TARGET)

.PHONY: all clean
Вложения
ff_splitter.zip
(680.35 КБ) 206 скачиваний
ff_splitter.zip
(680.35 КБ) 206 скачиваний
ya
^-^
Сообщения: 2994
Зарегистрирован: 16 дек 2021, 19:56

Re: Сохранить фрагмент из видеофайла (нарезка на фрагменты)

Сообщение ya »

Необходимые пакеты для сборки

Код: Выделить всё

apt install make ffmpeg g++

Код: Выделить всё

sudo apt-get install libjsoncpp-dev

Код: Выделить всё

apt-get install libjson-c-dev
сборка: Makefile

Код: Выделить всё

# Определяем компилятор и флаги компиляции
CXX = g++
CXXFLAGS = -std=c++11 -Wall -Wextra -ljson-c -I/usr/include/json-c
LDFLAGS = -L/usr/lib -ljson-c

# Основной целевой файл
TARGET = splitter

# Исходные файлы
SRCS = splitter3.cpp  # Замените 'main.cpp' на имя вашего файла с кодом

# Объектные файлы
OBJS = $(SRCS:.cpp=.o)

# Правило по умолчанию для сборки
all: $(TARGET)

# Линковка объекта в исполняемый файл
$(TARGET): $(OBJS)
        $(CXX) $(OBJS) -o $(TARGET)

# Правило для компиляции исходников в объектные файлы
%.o: %.cpp
        $(CXX) $(CXXFLAGS) $(LDFLAGS) -c $< -o $@

# Правило для очистки
clean:
        rm -f $(OBJS) $(TARGET)

.PHONY: all clean
ya
^-^
Сообщения: 2994
Зарегистрирован: 16 дек 2021, 19:56

Re: Сохранить фрагмент из видеофайла (нарезка на фрагменты)

Сообщение ya »

необходимые пакеты для сборки

Код: Выделить всё

sudo apt install make g++ pkg-config libjson-c-dev
замена пробелов на табуляторы в Makefile в начале каждой строки

Код: Выделить всё

sed -i 's/^  */\t/' Makefile
Makefile

Код: Выделить всё

CXX = g++
CXXFLAGS = -std=c++11 -Wall -Wextra $(shell pkg-config --cflags json-c)
LDFLAGS = $(shell pkg-config --libs json-c)

TARGET = splitter
SRCS = splitter4.cpp
OBJS = $(SRCS:.cpp=.o)

all: $(TARGET)

$(TARGET): $(OBJS)
        $(CXX) $(OBJS) -o $(TARGET) $(LDFLAGS)

%.o: %.cpp
        $(CXX) $(CXXFLAGS) -c $< -o $@

clean:
        rm -f $(OBJS) $(TARGET)

splitter4.cpp

Код: Выделить всё

#include <iostream>
#include <iomanip>
#include <sstream>
#include <vector>
#include <string>
#include <fstream>
#include <cstdlib>
#include <algorithm>
#include <ctime>
#include <tuple>
#include <json-c/json.h>

class Time {
private:
    int hours, minutes;
    double seconds;

public:
    Time(int h = 0, int m = 0, double s = 0.0) : hours(h), minutes(m), seconds(s) {}

    std::string toString() const {
        std::stringstream ss;
        ss << std::setw(2) << std::setfill('0') << hours << ":"
           << std::setw(2) << std::setfill('0') << minutes << ":"
           << std::fixed << std::setprecision(6) << std::setfill('0') << seconds;
        return ss.str();
    }

    bool operator<(const Time& other) const {
        return std::tie(hours, minutes, seconds) < std::tie(other.hours, other.minutes, other.seconds);
    }

    bool operator>(const Time& other) const {
        return std::tie(hours, minutes, seconds) > std::tie(other.hours, other.minutes, other.seconds);
    }

    void subtract(double sec) {
        seconds -= sec;
        while (seconds < 0) { seconds += 60; minutes--; }
        while (minutes < 0) { minutes += 60; hours--; }
        if (hours < 0) { hours = minutes = 0; seconds = 0.0; }
    }

    bool isValid() const { return (hours >= 0 && minutes >= 0 && seconds >= 0); }
    double getSeconds() const { return seconds; }
    double getTotalSeconds() const { return hours * 3600 + minutes * 60 + seconds; }
};

class FrameExtractor {
public:
    static bool runFFProbe(const std::string& input_file, const std::string& output_json) {
        // Запрашиваем только первый видеопоток и только ключевые кадры
        std::string command = "ffprobe -v error -select_streams v:0 -skip_frame nokey "
                              "-show_entries frame=best_effort_timestamp_time "
                              "-of json \"" + input_file + "\" > \"" + output_json + "\"";
        return system(command.c_str()) == 0;
    }

    static void formatTimestamps(const std::string& json_file, const std::string& txt_file) {
        std::ifstream infile(json_file);
        if (!infile.is_open()) return;
        std::string content((std::istreambuf_iterator<char>(infile)), std::istreambuf_iterator<char>());
        infile.close();

        struct json_object *parsed_json = json_tokener_parse(content.c_str());
        struct json_object *frames;

        if (parsed_json && json_object_object_get_ex(parsed_json, "frames", &frames)) {
            std::ofstream outfile(txt_file);
            size_t n_frames = json_object_array_length(frames);

            for (size_t i = 0; i < n_frames; i++) {
                struct json_object *frame = json_object_array_get_idx(frames, i);
                struct json_object *ts_time;
                if (json_object_object_get_ex(frame, "best_effort_timestamp_time", &ts_time)) {
                    outfile << json_object_get_string(ts_time) << std::endl;
                }
            }
        }
        if (parsed_json) json_object_put(parsed_json);
    }
};

class TimeUtils {
public:
    static std::string getCurrentDateTimeString() {
        std::time_t now = std::time(nullptr);
        std::tm* local_time = std::localtime(&now);
        std::ostringstream oss;
        oss << std::put_time(local_time, "%Y.%m.%d_%H-%M-%S");
        return oss.str();
    }

    static std::string getFileExtension(const std::string& filename) {
        size_t dot_pos = filename.rfind('.');
        return (dot_pos != std::string::npos) ? filename.substr(dot_pos) : "";
    }
};

class TimestampFileParser {
public:
    static std::vector<Time> readKeyframesFromFile(const std::string& filename) {
        std::ifstream file(filename);
        std::vector<Time> timestamps;
        std::string line;
        while (std::getline(file, line)) {
            Time time = parseTime(line);
            if (time.isValid()) timestamps.push_back(time);
        }
        return timestamps;
    }

    static Time findMaxTimeLessThan(const std::vector<Time>& timestamps, const Time& user_time) {
        Time max_time(-1, -1, -1);
        double user_total = user_time.getTotalSeconds();
        
        for (const auto& current_time : timestamps) {
            if (current_time.getTotalSeconds() < user_total) {
                if (!max_time.isValid() || current_time.getTotalSeconds() > max_time.getTotalSeconds()) {
                    max_time = current_time;
                }
            }
        }
        return max_time;
    }

    static Time parseTime(const std::string& s) {
        if (s.empty()) return Time(-1, -1, -1);
        // Если это просто число (секунды)
        if (s.find(':') == std::string::npos) {
            try {
                double ts = std::stod(s);
                int h = (int)(ts / 3600);
                int m = (int)((ts - h * 3600) / 60);
                double sec = ts - h * 3600 - m * 60;
                return Time(h, m, sec);
            } catch (...) { return Time(-1, -1, -1); }
        }
        // Если это формат H:M:S
        int h, m; double sec; char c1, c2;
        std::istringstream ss(s);
        if (ss >> h >> c1 >> m >> c2 >> sec) return Time(h, m, sec);
        return Time(-1, -1, -1);
    }
};

class Application {
private:
    std::string input_file;
    std::string keyframe_txt;
    std::string keyframe_json;

public:
    Application(const std::string& file) : input_file(file) {
        keyframe_txt = input_file + "_keyframes.txt";
        keyframe_json = input_file + "_keyframes.json";
    }

    void process(const std::string& search_time_str, const std::string& end_time_str) {
        if (search_time_str == "0") {
            generateAndRunFFmpegCommand(end_time_str);
            return;
        }

        if (!checkIfFileExists(keyframe_txt)) {
            extractKeyframes();
        }

        Time user_time = TimestampFileParser::parseTime(search_time_str);
        auto timestamps = TimestampFileParser::readKeyframesFromFile(keyframe_txt);
        Time max_time = TimestampFileParser::findMaxTimeLessThan(timestamps, user_time);

        if (max_time.isValid()) {
            handleFoundTime(max_time, end_time_str);
        } else {
            std::cout << "No keyframe found before " << user_time.toString() << ". Using start." << std::endl;
            handleFoundTime(Time(0,0,0.1), end_time_str); // Подстраховка
        }
    }

private:
    void extractKeyframes() {
        std::cout << "Extracting keyframes to JSON..." << std::endl;
        if (!FrameExtractor::runFFProbe(input_file, keyframe_json)) {
            throw std::runtime_error("Error running ffprobe.");
        }
        std::cout << "Converting JSON to TXT..." << std::endl;
        FrameExtractor::formatTimestamps(keyframe_json, keyframe_txt);
    }

    void handleFoundTime(Time max_time, const std::string& end_time_str) {
        max_time.subtract(0.1); // Сдвиг для безопасной склейки
        std::string output_file = generateOutputFilename();
        std::string command = "ffmpeg -v error -ss " + max_time.toString() + 
                              " -i \"" + input_file + "\" " + 
                              (end_time_str.empty() ? "" : "-to " + end_time_str + " ") +
                              "-map_metadata -1 -c copy -avoid_negative_ts make_zero \"" + output_file + "\"";

        std::cout << "Executing: " << command << std::endl;
        if (system(command.c_str()) != 0) throw std::runtime_error("FFmpeg failed.");
        std::cout << "Done! Saved as: " << output_file << std::endl;
    }

    void generateAndRunFFmpegCommand(const std::string& end_time_str) {
        std::string output_file = generateOutputFilename();
        std::string command = "ffmpeg -v error -i \"" + input_file + "\" -map_metadata -1 " +
                              (end_time_str.empty() ? "" : "-to " + end_time_str + " ") +
                              "-c copy \"" + output_file + "\"";
        if (system(command.c_str()) != 0) throw std::runtime_error("FFmpeg failed.");
    }

    std::string generateOutputFilename() {
        std::string ext = TimeUtils::getFileExtension(input_file);
        return input_file.substr(0, input_file.size() - ext.size()) + "." + TimeUtils::getCurrentDateTimeString() + ext;
    }

    bool checkIfFileExists(const std::string& f) { std::ifstream file(f); return file.good(); }
};

int main(int argc, char* argv[]) {
    if (argc < 3) {
        std::cerr << "Usage: " << argv[0] << " <input_file> <time_start> [time_end]" << std::endl;
        return 1;
    }
    try {
        Application app(argv[1]);
        app.process(argv[2], (argc > 3 ? argv[3] : ""));
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    return 0;
}
сборка

Код: Выделить всё

make clean
make
ya
^-^
Сообщения: 2994
Зарегистрирован: 16 дек 2021, 19:56

Re: Сохранить фрагмент из видеофайла (нарезка на фрагменты)

Сообщение ya »

splitter4.cpp

Код: Выделить всё

#include <iostream>
#include <iomanip>
#include <sstream>
#include <vector>
#include <string>
#include <fstream>
#include <cstdlib>
#include <algorithm>
#include <ctime>
#include <tuple>
#include <json-c/json.h>

class Time {
private:
    int hours, minutes;
    double seconds;

public:
    Time(int h = 0, int m = 0, double s = 0.0) : hours(h), minutes(m), seconds(s) {}

    std::string toString() const {
        std::stringstream ss;
        ss << std::setw(2) << std::setfill('0') << hours << ":"
           << std::setw(2) << std::setfill('0') << minutes << ":"
           << std::fixed << std::setprecision(6) << std::setfill('0') << seconds;
        return ss.str();
    }

    bool operator<(const Time& other) const {
        return std::tie(hours, minutes, seconds) < std::tie(other.hours, other.minutes, other.seconds);
    }

    bool operator>(const Time& other) const {
        return std::tie(hours, minutes, seconds) > std::tie(other.hours, other.minutes, other.seconds);
    }

    void subtract(double sec) {
        seconds -= sec;
        while (seconds < 0) { seconds += 60; minutes--; }
        while (minutes < 0) { minutes += 60; hours--; }
        if (hours < 0) { hours = minutes = 0; seconds = 0.0; }
    }

    bool isValid() const { return (hours >= 0 && minutes >= 0 && seconds >= 0); }
    double getSeconds() const { return seconds; }
    double getTotalSeconds() const { return hours * 3600 + minutes * 60 + seconds; }
};

class FrameExtractor {
public:
    static bool runFFProbe(const std::string& input_file, const std::string& output_json) {
        // Запрашиваем только первый видеопоток и только ключевые кадры
        std::string command = "ffprobe -v error -select_streams v:0 -skip_frame nokey "
                              "-show_entries frame=best_effort_timestamp_time "
                              "-of json \"" + input_file + "\" > \"" + output_json + "\"";
        return system(command.c_str()) == 0;
    }

    static void formatTimestamps(const std::string& json_file, const std::string& txt_file) {
        std::ifstream infile(json_file);
        if (!infile.is_open()) return;
        std::string content((std::istreambuf_iterator<char>(infile)), std::istreambuf_iterator<char>());
        infile.close();

        struct json_object *parsed_json = json_tokener_parse(content.c_str());
        struct json_object *frames;

        if (parsed_json && json_object_object_get_ex(parsed_json, "frames", &frames)) {
            std::ofstream outfile(txt_file);
            size_t n_frames = json_object_array_length(frames);

            for (size_t i = 0; i < n_frames; i++) {
                struct json_object *frame = json_object_array_get_idx(frames, i);
                struct json_object *ts_time;
                if (json_object_object_get_ex(frame, "best_effort_timestamp_time", &ts_time)) {
                    outfile << json_object_get_string(ts_time) << std::endl;
                }
            }
        }
        if (parsed_json) json_object_put(parsed_json);
    }
};

class TimeUtils {
public:
    static std::string getCurrentDateTimeString() {
        std::time_t now = std::time(nullptr);
        std::tm* local_time = std::localtime(&now);
        std::ostringstream oss;
        oss << std::put_time(local_time, "%Y.%m.%d_%H-%M-%S");
        return oss.str();
    }

    static std::string getFileExtension(const std::string& filename) {
        size_t dot_pos = filename.rfind('.');
        return (dot_pos != std::string::npos) ? filename.substr(dot_pos) : "";
    }
};

class TimestampFileParser {
public:
    static std::vector<Time> readKeyframesFromFile(const std::string& filename) {
        std::ifstream file(filename);
        std::vector<Time> timestamps;
        std::string line;
        while (std::getline(file, line)) {
            Time time = parseTime(line);
            if (time.isValid()) timestamps.push_back(time);
        }
        return timestamps;
    }

    static Time findMaxTimeLessThan(const std::vector<Time>& timestamps, const Time& user_time) {
        Time max_time(-1, -1, -1);
        double user_total = user_time.getTotalSeconds();

        for (const auto& current_time : timestamps) {
            if (current_time.getTotalSeconds() < user_total) {
                if (!max_time.isValid() || current_time.getTotalSeconds() > max_time.getTotalSeconds()) {
                    max_time = current_time;
                }
            }
        }
        return max_time;
    }

    static Time parseTime(const std::string& s) {
        if (s.empty()) return Time(-1, -1, -1);
        // Если это просто число (секунды)
        if (s.find(':') == std::string::npos) {
            try {
                double ts = std::stod(s);
                int h = (int)(ts / 3600);
                int m = (int)((ts - h * 3600) / 60);
                double sec = ts - h * 3600 - m * 60;
                return Time(h, m, sec);
            } catch (...) { return Time(-1, -1, -1); }
        }
        // Если это формат H:M:S
        int h, m; double sec; char c1, c2;
        std::istringstream ss(s);
        if (ss >> h >> c1 >> m >> c2 >> sec) return Time(h, m, sec);
        return Time(-1, -1, -1);
    }
};

class Application {
private:
    std::string input_file;
    std::string keyframe_txt;
    std::string keyframe_json;

public:
    Application(const std::string& file) : input_file(file) {
        keyframe_txt = input_file + "_keyframes.txt";
        keyframe_json = input_file + "_keyframes.json";
    }

    void process(const std::string& search_time_str, const std::string& end_time_str) {
        if (search_time_str == "0") {
            generateAndRunFFmpegCommand(end_time_str);
            return;
        }

        if (!checkIfFileExists(keyframe_txt)) {
            extractKeyframes();
        }

        Time user_time = TimestampFileParser::parseTime(search_time_str);
        auto timestamps = TimestampFileParser::readKeyframesFromFile(keyframe_txt);
        Time max_time = TimestampFileParser::findMaxTimeLessThan(timestamps, user_time);

        if (max_time.isValid()) {
            handleFoundTime(max_time, end_time_str);
        } else {
            std::cout << "No keyframe found before " << user_time.toString() << ". Using start." << std::endl;
            handleFoundTime(Time(0,0,0.1), end_time_str); // Подстраховка
        }
    }

private:
    void extractKeyframes() {
        std::cout << "Extracting keyframes to JSON..." << std::endl;
        if (!FrameExtractor::runFFProbe(input_file, keyframe_json)) {
            throw std::runtime_error("Error running ffprobe.");
        }
        std::cout << "Converting JSON to TXT..." << std::endl;
        FrameExtractor::formatTimestamps(keyframe_json, keyframe_txt);
    }
/*
    void handleFoundTime(Time max_time, const std::string& end_time_str) {
        max_time.subtract(0.1); // Сдвиг для безопасной склейки
        std::string output_file = generateOutputFilename();
        std::string command = "ffmpeg -v error -ss " + max_time.toString() +
                              " -i \"" + input_file + "\" " +
                              (end_time_str.empty() ? "" : "-to " + end_time_str + " ") +
                              "-map_metadata -1 -c copy -avoid_negative_ts make_zero \"" + output_file + "\"";

        std::cout << "Executing: " << command << std::endl;
        if (system(command.c_str()) != 0) throw std::runtime_error("FFmpeg failed.");
        std::cout << "Done! Saved as: " << output_file << std::endl;
    }
*/
void handleFoundTime(Time max_time, const std::string& end_time_str) {
    max_time.subtract(0.1);
    std::string output_file = generateOutputFilename();

    std::string duration_part = "";
    if (!end_time_str.empty()) {
        Time end_time = TimestampFileParser::parseTime(end_time_str);
        if (end_time.isValid()) {
            // Вычисляем длительность: Конец - Начало
            double diff = end_time.getTotalSeconds() - max_time.getTotalSeconds();
            if (diff > 0) {
                duration_part = " -t " + std::to_string(diff);
            }
        }
    }

    // Формируем команду: -ss до -i для скорости, -t для точной длительности
    std::string command = "ffmpeg -v error -ss " + max_time.toString() +
                          " -i \"" + input_file + "\" " +
                          duration_part +
                          " -map_metadata -1 -c copy -avoid_negative_ts make_zero \"" + output_file + "\"";

    std::cout << "Executing: " << command << std::endl;
    if (system(command.c_str()) != 0) throw std::runtime_error("FFmpeg failed.");
}

/*
    void generateAndRunFFmpegCommand(const std::string& end_time_str) {
        std::string output_file = generateOutputFilename();
        std::string command = "ffmpeg -v error -i \"" + input_file + "\" -map_metadata -1 " +
                              (end_time_str.empty() ? "" : "-to " + end_time_str + " ") +
                              "-c copy \"" + output_file + "\"";
        if (system(command.c_str()) != 0) throw std::runtime_error("FFmpeg failed.");
    }
*/
void generateAndRunFFmpegCommand(const std::string& end_time_str) {
    std::string output_file = generateOutputFilename();
    std::string limit = "";
    if (!end_time_str.empty()) {
        limit = " -to " + end_time_str; // Здесь -to работает корректно, так как нет -ss
    }

    std::string command = "ffmpeg -v error -i \"" + input_file + "\" -map_metadata -1" +
                          limit + " -c copy \"" + output_file + "\"";
    if (system(command.c_str()) != 0) throw std::runtime_error("FFmpeg failed.");
}

    std::string generateOutputFilename() {
        std::string ext = TimeUtils::getFileExtension(input_file);
        return input_file.substr(0, input_file.size() - ext.size()) + "." + TimeUtils::getCurrentDateTimeString() + ext;
    }

    bool checkIfFileExists(const std::string& f) { std::ifstream file(f); return file.good(); }
};

int main(int argc, char* argv[]) {
    if (argc < 3) {
        std::cerr << "Usage: " << argv[0] << " <input_file> <time_start> [time_end]" << std::endl;
        return 1;
    }
    try {
        Application app(argv[1]);
        app.process(argv[2], (argc > 3 ? argv[3] : ""));
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    return 0;
}
ya
^-^
Сообщения: 2994
Зарегистрирован: 16 дек 2021, 19:56

Re: Сохранить фрагмент из видеофайла (нарезка на фрагменты)

Сообщение ya »

splitter5.cpp

Код: Выделить всё

#include <iostream>
#include <iomanip>
#include <sstream>
#include <vector>
#include <string>
#include <fstream>
#include <cstdlib>
#include <algorithm>
#include <ctime>
#include <tuple>
#include <json-c/json.h>

// Класс для корректной работы с временем
class Time {
private:
    int hours, minutes;
    double seconds;

public:
    Time(int h = 0, int m = 0, double s = 0.0) {
        if (h < 0 && m < 0 && s < 0) { // Для инициализации невалидного состояния
            hours = -1; minutes = -1; seconds = -1.0;
            return;
        }
        double total = h * 3600.0 + m * 60.0 + s;
        if (total < 0) total = 0;
        hours = static_cast<int>(total / 3600);
        minutes = static_cast<int>((total - hours * 3600) / 60);
        seconds = total - (hours * 3600) - (minutes * 60);
    }

    std::string toString() const {
        std::stringstream ss;
        ss << std::setw(2) << std::setfill('0') << hours << ":"
           << std::setw(2) << std::setfill('0') << minutes << ":"
           << std::fixed << std::setprecision(6) << std::setfill('0') << seconds;
        return ss.str();
    }

    bool isValid() const { return hours >= 0; }
    double getTotalSeconds() const { return hours * 3600.0 + minutes * 60.0 + seconds; }

    void subtract(double sec) {
        double total = getTotalSeconds() - sec;
        *this = Time(0, 0, total);
    }
};

class TimeUtils {
public:
    static std::string getCurrentDateTimeString() {
        std::time_t now = std::time(nullptr);
        std::tm* local_time = std::localtime(&now);
        std::ostringstream oss;
        oss << std::put_time(local_time, "%Y.%m.%d_%H-%M-%S");
        return oss.str();
    }
};

class FrameExtractor {
public:
    static bool runFFProbe(const std::string& input, const std::string& output) {
        std::string cmd = "ffprobe -v error -select_streams v:0 -skip_frame nokey "
                          "-show_entries frame=best_effort_timestamp_time "
                          "-of json \"" + input + "\" > \"" + output + "\"";
        return system(cmd.c_str()) == 0;
    }

    static void formatTimestamps(const std::string& json_f, const std::string& txt_f) {
        std::ifstream infile(json_f);
        if (!infile.is_open()) return;
        std::string content((std::istreambuf_iterator<char>(infile)), std::istreambuf_iterator<char>());
        infile.close();

        struct json_object *parsed_json = json_tokener_parse(content.c_str());
        struct json_object *frames;
        if (parsed_json && json_object_object_get_ex(parsed_json, "frames", &frames)) {
            std::ofstream outfile(txt_f);
            for (size_t i = 0; i < json_object_array_length(frames); i++) {
                struct json_object *frame = json_object_array_get_idx(frames, i);
                struct json_object *ts;
                if (json_object_object_get_ex(frame, "best_effort_timestamp_time", &ts))
                    outfile << json_object_get_string(ts) << std::endl;
            }
        }
        if (parsed_json) json_object_put(parsed_json);
    }
};

class TimestampFileParser {
public:
    static Time parseTime(const std::string& s) {
        if (s.empty()) return Time(-1, -1, -1);
        if (s.find(':') == std::string::npos) {
            try { return Time(0, 0, std::stod(s)); } catch (...) { return Time(-1, -1, -1); }
        }
        int h, m; double sec; char c1, c2;
        std::istringstream ss(s);
        if (ss >> h >> c1 >> m >> c2 >> sec) return Time(h, m, sec);
        return Time(-1, -1, -1);
    }

    static std::vector<Time> readKeyframes(const std::string& f) {
        std::ifstream file(f);
        std::vector<Time> tms;
        std::string line;
        while (std::getline(file, line)) {
            Time t = parseTime(line);
            if (t.isValid()) tms.push_back(t);
        }
        return tms;
    }
};

class Application {
    std::string input_file, key_txt, key_json;
public:
    Application(const std::string& f) : input_file(f) {
        key_txt = f + "_keyframes.txt";
        key_json = f + "_keyframes.json";
    }

    void process(const std::string& start_s, const std::string& end_s) {
        if (!checkFile(key_txt)) {
            std::cout << "[Info] Scanning for keyframes..." << std::endl;
            FrameExtractor::runFFProbe(input_file, key_json);
            FrameExtractor::formatTimestamps(key_json, key_txt);
        }

        Time user_start = TimestampFileParser::parseTime(start_s);
        auto keys = TimestampFileParser::readKeyframes(key_txt);
        
        Time best_start(0,0,0);
        for (auto& k : keys) {
            if (k.getTotalSeconds() < user_start.getTotalSeconds()) best_start = k;
            else break;
        }
        
        best_start.subtract(0.1);
        std::string ext = input_file.substr(input_file.find_last_of("."));
        std::string output = input_file.substr(0, input_file.find_last_of(".")) + "." + TimeUtils::getCurrentDateTimeString() + ext;
        
        std::string dur_cmd = "";
        double duration = 0;
        if (!end_s.empty()) {
            Time user_end = TimestampFileParser::parseTime(end_s);
            duration = user_end.getTotalSeconds() - best_start.getTotalSeconds();
            if (duration > 0) dur_cmd = " -t " + std::to_string(duration);
        }

        std::cout << "\n==========================================" << std::endl;
        std::cout << "   РЕЗКА ВИДЕО ПО КЛЮЧЕВЫМ КАДРАМ" << std::endl;
        std::cout << "==========================================" << std::endl;
        std::cout << "Старт (ключевой): " << best_start.toString() << std::endl;
        std::cout << "Длительность:     " << (duration > 0 ? Time(0,0,duration).toString() : "До конца") << std::endl;
        std::cout << "Выходной файл:    " << output << std::endl;
        std::cout << "==========================================\n" << std::endl;

        std::string cmd = "ffmpeg -ss " + best_start.toString() + " -i \"" + input_file + "\"" + 
                          dur_cmd + " -map_metadata -1 -c copy -avoid_negative_ts make_zero \"" + output + "\"";
        
        system(cmd.c_str());
        std::cout << "\n[Success] Готово!" << std::endl;
    }
    bool checkFile(const std::string& f) { std::ifstream ifs(f); return ifs.good(); }
};

int main(int argc, char** argv) {
    if (argc < 3) {
        std::cerr << "Usage: " << argv[0] << " <file> <start_time> [end_time]" << std::endl;
        return 1;
    }
    try {
        Application app(argv[1]);
        app.process(argv[2], argc > 3 ? argv[3] : "");
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
    return 0;
}
Makefile

Код: Выделить всё

CXX = g++
CXXFLAGS = -std=c++11 -Wall -Wextra $(shell pkg-config --cflags json-c)
LDFLAGS = $(shell pkg-config --libs json-c)

TARGET = splitter
SRCS = splitter5.cpp
OBJS = $(SRCS:.cpp=.o)

all: $(TARGET)

$(TARGET): $(OBJS)
        $(CXX) $(OBJS) -o $(TARGET) $(LDFLAGS)

%.o: %.cpp
        $(CXX) $(CXXFLAGS) -c $< -o $@

clean:
        rm -f $(OBJS) $(TARGET)
Ответить