【ffmpeg C++ 播放器优化实战】优化你的视频播放器:使用策略模式和单例模式进行视频优化

简介: 【ffmpeg C++ 播放器优化实战】优化你的视频播放器:使用策略模式和单例模式进行视频优化

1. 引言

1.1 项目背景和挑战

在嵌入式领域,音视频处理是一个重要的应用场景。我们经常需要处理各种音视频数据,例如解码、播放、同步等。在这个过程中,我们可能会遇到各种挑战,例如性能问题、同步问题等。

在本项目中,我们使用了QT和FFmpeg来构建一个视频播放器。QT是一个跨平台的应用程序开发框架,它提供了丰富的GUI功能。FFmpeg是一个开源的音视频处理库,它提供了各种音视频编解码功能。

我们的视频播放器的基本流程是这样的:解码类解码数据,然后将解码后的数据增加到AVSync类的AVFrame队列中。然后,AVSync类负责对音视频数据进行同步和播放。

然而,我们发现在播放过程中有一些微小的卡顿。这可能是由于各种因素导致的,例如解码速度、数据同步、播放速度等。为了解决这个问题,我们需要引入一些优化方案。

1.2 优化方案的需求

在考虑优化方案时,我们有以下几个需求:

  1. 可以开启和关闭优化方案:我们希望能够灵活地开启和关闭优化方案,以便根据实际情况选择最适合的优化方案。
  2. 可以同时开启多种优化方案:我们希望能够同时开启多种优化方案,以实现最大的优化效果。
  3. 易于扩展:我们希望能够方便地添加新的优化方案,以便应对未来可能出现的新的挑战。

为了满足这些需求,我们决定使用设计模式来设计我们的优化方案。设计模式(Design Pattern)是一种在特定情况下解决软件设计问题的通用可重用解决方案。它不是可以直接转化为代码的完成设计,而是描述在各种不同情况下如何解决问题的模板。

在下一章节中,我们将讨论我们选择的设计模式,以及如何使用这些设计模式来设计我们的优化方案。

2. 设计思路

在这一章节中,我们将详细讨论如何使用策略模式(Strategy Pattern)和单例模式(Singleton Pattern)来设计和实现我们的视频优化类。我们将从高层次的设计思路开始,然后深入到具体的类设计和命名。

2.1 设计模式的选择:策略模式和单例模式

在面向对象编程中,设计模式(Design Patterns)是一种经过验证的解决特定问题的最佳实践。在我们的项目中,我们选择了策略模式和单例模式。

策略模式

策略模式是一种行为设计模式,它定义了一系列的算法,并将每一个算法封装起来,使得它们可以相互替换。策略模式让算法的变化独立于使用算法的客户。这种类型的设计模式属于行为型模式。

在我们的项目中,每种优化方案(如双缓冲、备份等)可以被视为一个策略。通过使用策略模式,我们可以在运行时动态地更改优化方案,从而提高视频播放的性能。

单例模式

单例模式是一种创建型设计模式,它保证一个类只有一个实例,并提供一个全局访问点。在我们的项目中,我们将使用单例模式来创建和管理OptimizationManager类的实例。

2.2 类的设计和命名

在设计类时,我们希望类名能够清晰地表达类的功能和责任。以下是我们为每个类选择的名字,以及每个类的主要职责:

类名 职责
OptimizationStrategy 定义所有优化策略的通用接口
DoubleBufferingStrategy 实现双缓冲策略
BackupStrategy 实现备份策略
OptimizationContext 提供所有策略可能需要的数据和操作
OptimizationManager 管理所有的优化策略

在接下来的章节中,我们将详细讨论每个类的设计和实现。

3. 代码设计

在这一章节中,我们将详细讨论代码的设计,包括使用C++标准的优势,以及OptimizationStrategy、DoubleBufferingStrategy、BackupStrategy、OptimizationContext和OptimizationManager这五个类的设计。

3.1 使用C++标准的优势

C++是一种静态类型、多范式、编译式的编程语言。C++标准(C++11、C++14、C++17、C++20)为我们提供了许多强大的功能和工具,使我们能够编写更高效、更安全、更易于维护的代码。

例如,C++11引入了智能指针(Smart Pointers),这是一种可以自动管理对象生命周期的工具。智能指针可以帮助我们避免内存泄漏(Memory Leaks)和悬挂指针(Dangling Pointers),这在嵌入式系统和音视频处理中是非常重要的。

C++14进一步增强了C++的功能,例如,它引入了泛型(Generic)编程的概念,使我们能够编写更灵活、更可重用的代码。泛型编程是一种编程范式,它依赖于参数化类型和函数,以实现在不同的类型上执行相同的操作。

C++17和C++20则引入了更多的新特性,例如并发(Concurrency)和并行(Parallelism)编程,模块(Modules),概念(Concepts)等等。这些新特性使我们能够编写更高效、更易于理解和维护的代码。

在我们的项目中,我们将充分利用C++标准提供的这些功能和工具。

3.2 OptimizationStrategy类的设计

在我们的设计中,OptimizationStrategy类是所有优化策略的基类,它定义了一个通用的接口,所有的具体策略类都需要实现这个接口。这是策略模式(Strategy Pattern)的核心思想,它允许我们在运行时切换不同的策略。

在C++中,我们通常使用抽象基类(Abstract Base Class,ABC)来定义接口。抽象基类是一种特殊的类,它不能被实例化,只能作为其他类的基类。抽象基类中可以定义纯虚函数(Pure Virtual Function),子类必须重写这些函数。

以下是OptimizationStrategy类的定义:

class OptimizationStrategy {
public:
    virtual ~OptimizationStrategy() = default;
    // 执行优化策略
    virtual void execute() = 0;
};

在这个类中,我们定义了一个纯虚函数execute,所有的具体策略类都需要重写这个函数。这个函数的作用是执行优化策略。

这种设计的优点是灵活性和扩展性。我们可以在运行时切换不同的优化策略,只需要改变一个指向OptimizationStrategy的指针即可。如果我们想添加新的优化策略,只需要定义一个新的类,继承自OptimizationStrategy,并重写execute函数即可。

这种设计符合开闭原则(Open-Closed Principle,OCP)。开闭原则是面向对象设计的一种重要原则,它要求软件实体(如类、模块、函数等)应该对扩展开放,对修改关闭。在我们的设计中,添加新的优化策略不需要修改现有的代码,只需要添加新的代码,这就是对扩展开放。同时,我们的设计不依赖于具体的优化策略,只依赖于OptimizationStrategy接口,这就是对修改关闭。

在实际的编程实践中,我们可能需要根据具体的需求和环境来调整和优化我们的设计。例如,我们可能需要添加一些新的方法到OptimizationStrategy接口中,或者我们可能需要定义一些新的接口,以支持更复杂的优化策略。但是,这种灵活性和扩展性是策略模式的一个重要优点,它可以帮助我们应对需求和环境的变化。

3.3 具体策略类的设计:DoubleBufferingStrategy和BackupStrategy

在策略模式中,具体策略类是实现了策略接口的类,它们提供了策略的具体实现。在我们的设计中,DoubleBufferingStrategyBackupStrategy类就是具体策略类,它们都继承自OptimizationStrategy类,并重写了execute方法。

DoubleBufferingStrategy类

双缓冲(Double Buffering)是一种常用的优化策略,它可以减少画面的闪烁和撕裂,提高画面的流畅性。在双缓冲中,我们使用两个缓冲区:一个用于显示,另一个用于绘制。当绘制完成后,我们交换这两个缓冲区。这样,用户总是看到一个完整的画面,而不是正在绘制的画面。

以下是DoubleBufferingStrategy类的定义:

class DoubleBufferingStrategy : public OptimizationStrategy {
public:
    void execute() override {
        // 实现双缓冲策略
    }
};

在这个类中,我们重写了execute方法,实现了双缓冲策略。具体的实现可能会根据你的需求和环境的不同而不同。

BackupStrategy类

备份(Backup)是另一种常用的优化策略,它可以提高数据的安全性和可靠性。在备份策略中,我们定期将重要的数据复制到另一个地方,以防止数据丢失或损坏。

以下是BackupStrategy类的定义:

class BackupStrategy : public OptimizationStrategy {
public:
    void execute() override {
        // 实现备份策略
    }
};

在这个类中,我们重写了execute方法,实现了备份策略。具体的实现可能会根据你的需求和环境的不同而不同。

这两个类的设计都很简单,但它们都符合策略模式的原则:它们都实现了策略接口,可以在运行时互相替换。这种设计提供了很大的灵活性,可以帮助我们应对需求和环境的变化。

3.4 OptimizationContext类的设计

OptimizationContext类是我们设计中的一个重要组成部分,它包含了所有策略可能需要的数据和操作。在策略模式中,上下文(Context)通常扮演着策略的使用者的角色,它维护了一个对策略对象的引用,可以通过这个引用来调用策略的方法。

在我们的设计中,OptimizationContext类的主要职责是提供一个统一的接口,供OptimizationManager类和具体策略类使用。这个接口包括一些方法,这些方法可以获取和设置数据,以及执行一些操作。

以下是OptimizationContext类的定义:

class OptimizationContext {
public:
    // 获取和设置数据的方法
    void setData(const Data& data) { this->data = data; }
    Data getData() const { return this->data; }
    // 执行操作的方法
    void doOperation() {
        // 实现操作
    }
private:
    Data data;  // 存储数据的成员变量
};

在这个类中,我们定义了一些方法,这些方法可以获取和设置数据,以及执行一些操作。具体的实现可能会根据你的需求和环境的不同而不同。

这种设计的优点是解耦。OptimizationContext类将数据和操作封装在一起,提供了一个统一的接口,这样,OptimizationManager类和具体策略类就不需要直接访问数据和操作,只需要通过OptimizationContext类的接口就可以完成它们的工作。这样,我们可以在不影响其他类的情况下,修改数据的存储方式,或者修改操作的实现。

3.5 OptimizationManager类的设计

OptimizationManager类是我们设计中的核心部分,它负责管理所有的优化策略。在这个类中,我们使用了单例模式(Singleton Pattern),这是一种创建型设计模式,它保证一个类只有一个实例,并提供一个全局访问点。

以下是OptimizationManager类的定义:

class OptimizationManager {
public:
    // 获取单例实例的方法
    static OptimizationManager& getInstance() {
        static OptimizationManager instance;
        return instance;
    }
    // 添加策略的方法
    void addStrategy(std::shared_ptr<OptimizationStrategy> strategy) {
        strategies.push_back(strategy);
    }
    // 删除策略的方法
    void removeStrategy(std::shared_ptr<OptimizationStrategy> strategy) {
        strategies.erase(std::remove(strategies.begin(), strategies.end(), strategy), strategies.end());
    }
    // 执行所有策略的方法
    void executeStrategies() {
        for (const auto& strategy : strategies) {
            strategy->execute();
        }
    }
private:
    // 私有构造函数和赋值运算符,防止被外部调用
    OptimizationManager() = default;
    OptimizationManager(const OptimizationManager&) = delete;
    OptimizationManager& operator=(const OptimizationManager&) = delete;
    std::vector<std::shared_ptr<OptimizationStrategy>> strategies;  // 存储策略的容器
};

在这个类中,我们定义了一个静态方法getInstance,用于获取单例实例。我们使用了C++11的魔术静态(Magic Static)特性,保证了线程安全性。我们还定义了添加策略、删除策略和执行所有策略的方法。我们使用了std::shared_ptr来管理策略对象的生命周期,这是一种智能指针,它可以自动释放不再需要的对象。

这种设计的优点是简洁性和易用性。我们提供了一个全局访问点,可以方便地获取到OptimizationManager的实例,然后通过这个实例来管理优化策略。我们还使用了智能指针来管理策略对象的生命周期,这可以避免内存泄漏和悬挂指针等问题。

4. 优化方案的对比和差异

在这一章节中,我们将详细讨论两种优化策略:双缓冲策略(Double Buffering Strategy)和备份策略(Backup Strategy)。我们将对比这两种策略的优势和适用场景,并通过代码示例来说明如何实现这两种策略。

4.1 双缓冲策略的优势和适用场景

双缓冲(Double Buffering)是一种常见的优化策略,它在计算机图形学和视频播放中广泛应用。双缓冲的主要思想是使用两个缓冲区(buffer):一个用于显示,另一个用于准备下一帧。当下一帧准备好时,两个缓冲区会交换角色。这种策略可以避免画面撕裂(screen tearing)和闪烁(flickering),从而提高视频播放的流畅性。

以下是一个简单的双缓冲策略的实现:

class DoubleBufferingStrategy : public OptimizationStrategy {
public:
    void optimize(OptimizationContext& context) override {
        // Prepare the next frame in the back buffer
        context.prepareNextFrame();
        // Swap the front buffer and the back buffer
        context.swapBuffers();
        // Display the next frame from the front buffer
        context.displayNextFrame();
    }
};

在这个示例中,optimize方法首先在后缓冲区(back buffer)中准备下一帧,然后交换前缓冲区(front buffer)和后缓冲区,最后从前缓冲区显示下一帧。这个过程在每一帧中都会重复,从而实现流畅的视频播放。

在视频播放的优化中,双缓冲策略的实现可能会有所不同。例如,在一些情况下,可能会有一个缓冲区用于存储解码后的帧,另一个缓冲区用于存储转换为RGB格式的帧。这样,解码和转换的过程可以在一个缓冲区中进行,而显示的过程则可以从另一个缓冲区中取出已经转换好的帧,从而实现双缓冲。

双缓冲策略的优势在于它可以提高视频播放的流畅性,特别是在处理高分辨率和高帧率的视频时。然而,双缓冲策略也有一些缺点。首先,它需要两个缓冲区,这可能会增加内存的使用。其次,如果视频数据的准备速度跟不上播放速度,可能会出现缓冲区交换时数据还未准备好的情况。因此,双缓冲策略更适用于那些对视频播放流畅性有高要求的场景。

4.2 备份策略的优势和适用场景

备份策略(Backup Strategy)是另一种常见的优化策略,它的主要思想是在处理数据时创建数据的备份。如果处理过程中出现错误,可以从备份中恢复数据,从而避免数据丢失。

以下是一个简单的备份策略的实现:

class BackupStrategy : public OptimizationStrategy {
public:
    void optimize(OptimizationContext& context) override {
        // Create a backup of the current frame
        context.backupCurrentFrame();
        // Process the current frame
        try {
            context.processCurrentFrame();
        } catch (...) {
            // If an error occurs, restore the current frame from the backup
            context.restoreCurrentFrame();
        }
    }
};

在这个示例中,optimize方法首先创建当前帧的备份,然后处理当前帧。如果处理过程中出现错误,会从备份中恢复当前帧。

备份策略的优势在于它可以提高数据处理的可靠性,特别是在处理可能出现错误的数据时。然而,备份策略也有一些缺点。首先,它需要创建数据的备份,这可能会增加内存的使用。其次,如果数据的大小很大,创建备份可能会花费较长的时间。因此,备份策略更适用于那些对数据可靠性有高要求的场景。

下图是我们的类设计图,可以帮助你更好地理解这两种策略的实现:

5. 如何使用OptimizationManager类进行视频优化

5.1 单例模式的应用

在我们的设计中,OptimizationManager类使用了单例模式。单例模式是一种创建型设计模式,它保证一个类只有一个实例,并提供一个全局访问点。

以下是如何在项目中使用OptimizationManager类的示例:

// 获取OptimizationManager的单例实例
OptimizationManager& manager = OptimizationManager::getInstance();
// 创建具体的策略对象
std::shared_ptr<OptimizationStrategy> doubleBufferingStrategy = std::make_shared<DoubleBufferingStrategy>();
std::shared_ptr<OptimizationStrategy> backupStrategy = std::make_shared<BackupStrategy>();
// 添加策略到OptimizationManager
manager.addStrategy(doubleBufferingStrategy);
manager.addStrategy(backupStrategy);
// 执行所有策略
manager.executeStrategies();

在这个示例中,我们首先获取了OptimizationManager的单例实例。然后,我们创建了两个具体的策略对象,并添加到了OptimizationManager中。最后,我们调用了executeStrategies方法,执行了所有的策略。

这种设计的优点是简洁性和易用性。我们提供了一个全局访问点,可以方便地获取到OptimizationManager的实例,然后通过这个实例来管理优化策略。我们还使用了智能指针来管理策略对象的生命周期,这可以避免内存泄漏和悬挂指针等问题。

5.2 管理优化策略的方法

在我们的设计中,OptimizationManager类提供了一系列的方法,用于管理优化策略。这些方法包括添加策略、删除策略和执行所有策略。

以下是如何使用这些方法的示例:

// 获取OptimizationManager的单例实例
OptimizationManager& manager = OptimizationManager::getInstance();
// 创建具体的策略对象
std::shared_ptr<OptimizationStrategy> doubleBufferingStrategy = std::make_shared<DoubleBufferingStrategy>();
std::shared_ptr<OptimizationStrategy> backupStrategy = std::make_shared<BackupStrategy>();
// 添加策略到OptimizationManager
manager.addStrategy(doubleBufferingStrategy);
manager.addStrategy(backupStrategy);
// 执行所有策略
manager.executeStrategies();
// 删除策略
manager.removeStrategy(doubleBufferingStrategy);

在这个示例中,我们首先获取了OptimizationManager的单例实例。然后,我们创建了两个具体的策略对象,并添加到了OptimizationManager中。接着,我们调用了executeStrategies方法,执行了所有的策略。最后,我们调用了removeStrategy方法,删除了一个策略。

这种设计的优点是灵活性和易用性。我们可以在运行时添加、删除和执行策略,这提供了很大的灵活性。我们还提供了一个简洁的接口,可以方便地管理优化策略,这提供了很大的易用性。

5.3 如何在项目中使用OptimizationManager

在你的项目中,OptimizationManager类可以作为优化策略的中心管理器,负责添加、删除和执行所有的优化策略。以下是如何在你的项目中使用OptimizationManager类的示例:

// 获取OptimizationManager的单例实例
OptimizationManager& manager = OptimizationManager::getInstance();
// 创建具体的策略对象
std::shared_ptr<OptimizationStrategy> doubleBufferingStrategy = std::make_shared<DoubleBufferingStrategy>();
std::shared_ptr<OptimizationStrategy> backupStrategy = std::make_shared<BackupStrategy>();
// 添加策略到OptimizationManager
manager.addStrategy(doubleBufferingStrategy);
manager.addStrategy(backupStrategy);
// 在适当的时机,执行所有策略
// 例如,在开始播放视频之前
manager.executeStrategies();
// 在不需要某个策略时,删除该策略
// 例如,在停止播放视频时
manager.removeStrategy(doubleBufferingStrategy);

在这个示例中,我们首先获取了OptimizationManager的单例实例。然后,我们创建了两个具体的策略对象,并添加到了OptimizationManager中。在开始播放视频之前,我们执行了所有的策略,以优化视频播放。在停止播放视频时,我们删除了不再需要的策略。

这种设计的优点是灵活性和易用性。我们可以在运行时添加、删除和执行策略,这提供了很大的灵活性。我们还提供了一个简洁的接口,可以方便地管理优化策略,这提供了很大的易用性。

5.4 设计的反思和总结

在我们的设计中,我们使用了策略模式和单例模式。策略模式提供了一种方式,使得我们可以在运行时切换不同的策略,而单例模式保证了我们的OptimizationManager类只有一个实例,并提供了一个全局访问点。

设计模式是一种解决常见问题的模板,它可以帮助我们编写更好的代码。然而,设计模式并不是银弹,它并不能解决所有的问题。在使用设计模式时,我们需要考虑我们的具体需求和环境,选择最适合的设计模式。

在我们的设计中,我们使用了策略模式,因为我们需要在运行时切换不同的优化策略。我们使用了单例模式,因为我们需要一个全局访问点,可以方便地获取到OptimizationManager的实例。

然而,这并不意味着我们的设计是完美的。在实际的编程实践中,我们可能需要根据我们的需求和环境的变化,对我们的设计进行调整和优化。例如,我们可能需要添加新的方法到OptimizationStrategy接口中,或者我们可能需要定义一些新的接口,以支持更复杂的优化策略。

总的来说,设计模式是一种强大的工具,它可以帮助我们编写更好的代码。然而,我们需要理解设计模式的原理和目的,才能正确地使用它。我们也需要理解我们的需求和环境,才能选择最适合的设计模式。

6. 扩展和优化:未来的可能性

6.1 后续扩展和优化

在我们的设计中,我们已经实现了一个灵活的优化策略管理系统,它可以在运行时切换不同的优化策略。然而,我们的设计还有很多可以扩展和优化的地方。

添加新的优化策略

我们可以根据我们的需求和环境,添加新的优化策略。例如,我们可以添加一个预加载策略(Preloading Strategy),它可以在播放视频之前预加载一部分数据,以减少缓冲时间。我们也可以添加一个负载均衡策略(Load Balancing Strategy),它可以在多个服务器之间分配任务,以提高性能。

使用组合模式

我们可以使用组合模式(Composite Pattern),以便能够同时执行多个优化策略。在组合模式中,我们可以创建一个复合策略(Composite Strategy),它包含多个子策略,并在执行时依次执行每个子策略。这样,我们可以将多个优化策略组合成一个更强大的优化策略。

使用观察者模式

我们可以使用观察者模式(Observer Pattern),以便在优化策略改变时通知相关的对象。在观察者模式中,我们可以创建一个观察者接口(Observer Interface),并让需要在优化策略改变时被通知的对象实现这个接口。然后,我们可以在OptimizationManager类中添加一个方法,用于注册观察者。当优化策略改变时,OptimizationManager类可以通知所有的观察者。

以上都是可能的扩展和优化方向,你需要根据你的具体需求和环境来选择最适合的方向。在实际的编程实践中,可能需要进行一些调整和优化,以达到最好的效果。

6.2 其他优化策略及其影响范围

在我们的设计中,我们可以根据我们的需求和环境,添加新的优化策略。以下是一些可能的优化策略,以及它们的影响范围:

预加载策略(Preloading Strategy)

预加载策略是一种优化策略,它可以在播放视频之前预加载一部分数据,以减少缓冲时间。这种策略的影响范围主要是视频播放的流畅性。通过预加载数据,我们可以减少视频播放时的缓冲时间,提高视频播放的流畅性。然而,这种策略也可能会增加网络带宽的使用量,因此我们需要根据我们的网络环境来调整预加载的数据量。

负载均衡策略(Load Balancing Strategy)

负载均衡策略是一种优化策略,它可以在多个服务器之间分配任务,以提高性能。这种策略的影响范围主要是系统的性能和稳定性。通过负载均衡,我们可以有效地利用我们的服务器资源,提高系统的性能。同时,通过将任务分配到多个服务器,我们也可以提高系统的稳定性,因为即使一个服务器出现问题,其他的服务器还可以继续处理任务。

缓存策略(Caching Strategy)

缓存策略是一种优化策略,它可以将经常使用的数据存储在快速访问的存储介质(如内存)中,以减少数据访问的时间。这种策略的影响范围主要是数据访问的速度。通过缓存数据,我们可以减少数据访问的时间,提高数据访问的速度。然而,这种策略也可能会增加内存的使用量,因此我们需要根据我们的内存环境来调整缓存的数据量。

以上都是可能的优化策略,你需要根据你的具体需求和环境来选择最适合的策略。在实际的编程实践中,可能需要进行一些调整和优化,以达到最好的效果。

结语

在我们的编程学习之旅中,理解是我们迈向更高层次的重要一步。然而,掌握新技能、新理念,始终需要时间和坚持。从心理学的角度看,学习往往伴随着不断的试错和调整,这就像是我们的大脑在逐渐优化其解决问题的“算法”。

这就是为什么当我们遇到错误,我们应该将其视为学习和进步的机会,而不仅仅是困扰。通过理解和解决这些问题,我们不仅可以修复当前的代码,更可以提升我们的编程能力,防止在未来的项目中犯相同的错误。

我鼓励大家积极参与进来,不断提升自己的编程技术。无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力。

相关实践学习
部署高可用架构
本场景主要介绍如何使用云服务器ECS、负载均衡SLB、云数据库RDS和数据传输服务产品来部署多可用区高可用架构。
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
9天前
|
XML 编解码 JSON
FFmpeg常用命令讲解及实战二(2)
FFmpeg常用命令讲解及实战二
32 0
|
9天前
|
Web App开发 编解码 安全
视频会议技术 入门探究:WebRTC、Qt与FFmpeg在视频编解码中的应用
视频会议技术 入门探究:WebRTC、Qt与FFmpeg在视频编解码中的应用
195 4
|
9天前
|
设计模式 安全 测试技术
【C/C++ 设计模式 单例】单例模式的选择策略:何时使用,何时避免
【C/C++ 设计模式 单例】单例模式的选择策略:何时使用,何时避免
68 0
|
9天前
|
设计模式 安全 算法
【C++入门到精通】特殊类的设计 | 单例模式 [ C++入门 ]
【C++入门到精通】特殊类的设计 | 单例模式 [ C++入门 ]
19 0
|
9天前
|
编解码 C语言
FFMPEG 获取视频PTS
FFMPEG 获取视频PTS
15 0
|
9天前
|
Web App开发 编解码 vr&ar
使用FFmpeg从音视频处理到流媒体技术的探索和实战应用
使用FFmpeg从音视频处理到流媒体技术的探索和实战应用
53 2
|
9天前
|
设计模式 Java C++
【C++高阶(八)】单例模式&特殊类的设计
【C++高阶(八)】单例模式&特殊类的设计
|
9天前
|
设计模式 安全 编译器
【代码片段】【C++】C++11线程安全单例模式
【代码片段】【C++】C++11线程安全单例模式
17 1
|
9天前
|
存储 数据处理 API
ffmpeg过滤器filter理论与实战
ffmpeg过滤器filter理论与实战
21 0
|
9天前
|
编解码 vr&ar 内存技术
FFmpeg常用命令讲解及实战二(1)
FFmpeg常用命令讲解及实战二
22 0