《Android FFmpeg 播放器开发梳理》第零章 基础公共类的封装

《Android FFmpeg 播放器开发梳理》第零章 基础公共类的封装

OpenGL小彩虹2021-07-11 9:54:0290A+A-

本文已获得作者授权,本文作者:cain_huang原文链接:https://www.jianshu.com/p/9003caa6683f

在开始介绍播放器开发之前,我们首先对posix库进行一定的封装,得到我们想要的 MutexConditionThread等类。

至于为何不用 C++11自带的相关类呢?

这是考虑到编译环境的问题,有些公司可能仍旧没升级 NDK 的版本,不支持C++11,这里为了方便,只好利用 Posix 封装一套 Thread 相关的基础类,部分代码参考(copy)自Android 源码中的代码。

至于原理,这里就不介绍了,网上相关资料还是很多的,分析互斥锁、条件锁等原理不是本文章的重点。

Mutex封装

Mutex 的封装可参考 Android 的 libutil 库里面的代码,直接复制过来使用即可,代码里面还封装了 AutoLock。代码如下:

 1#ifndef MUTEX_H 2#define MUTEX_H 3#include <stdint.h> 4#include <sys/types.h> 5#include <time.h> 6 7#include <pthread.h> 8 9typedef int32_t     status_t;1011class Condition;1213class Mutex {14public:15    enum {16        PRIVATE = 0,17        SHARED = 118    };19    Mutex();20    Mutex(const char* name);21    Mutex(int type, const char* name = NULL);22    ~Mutex();2324    // lock or unlock the mutex25    status_t    lock();26    void        unlock();2728    // lock if possible; returns 0 on success, error otherwise29    status_t    tryLock();3031    // Manages the mutex automatically. It'll be locked when Autolock is32    // constructed and released when Autolock goes out of scope.33    class Autolock {34    public:35        inline Autolock(Mutex& mutex) : mLock(mutex)  { mLock.lock(); }36        inline Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); }37        inline ~Autolock() { mLock.unlock(); }38    private:39        Mutex& mLock;40    };4142private:43    friend class Condition;4445    // A mutex cannot be copied46    Mutex(const Mutex&);47    Mutex&      operator = (const Mutex&);4849    pthread_mutex_t mMutex;50};5152inline Mutex::Mutex() {53    pthread_mutex_init(&mMutex, NULL);54}5556inline Mutex::Mutex(const char* name) {57    pthread_mutex_init(&mMutex, NULL);58}5960inline Mutex::Mutex(int type, const char* name) {61    if (type == SHARED) {62        pthread_mutexattr_t attr;63        pthread_mutexattr_init(&attr);64        pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);65        pthread_mutex_init(&mMutex, &attr);66        pthread_mutexattr_destroy(&attr);67    } else {68        pthread_mutex_init(&mMutex, NULL);69    }70}7172inline Mutex::~Mutex() {73    pthread_mutex_destroy(&mMutex);74}7576inline status_t Mutex::lock() {77    return -pthread_mutex_lock(&mMutex);78}7980inline void Mutex::unlock() {81    pthread_mutex_unlock(&mMutex);82}8384inline status_t Mutex::tryLock() {85    return -pthread_mutex_trylock(&mMutex);86}87typedef Mutex::Autolock AutoMutex;8889#endif //MUTEX_H

Condition封装

Condition类的封装跟 Mutex一样,直接从 Android 源码里面复制过来,稍作修改即可。

代码如下:

 1#ifndef CONDITION_H 2#define CONDITION_H 3 4#include <stdint.h> 5#include <sys/types.h> 6#include <time.h> 7#include <pthread.h> 8 9#include <Mutex.h>1011typedef int64_t nsecs_t; // nano-seconds1213class Condition {14public:15    enum {16        PRIVATE = 0,17        SHARED = 118    };1920    enum WakeUpType {21        WAKE_UP_ONE = 0,22        WAKE_UP_ALL = 123    };2425    Condition();26    Condition(int type);27    ~Condition();2829    status_t wait(Mutex& mutex);30    status_t waitRelative(Mutex& mutex, nsecs_t reltime);31    void signal();32    void signal(WakeUpType type) {33        if (type == WAKE_UP_ONE) {34            signal();35        } else {36            broadcast();37        }38    }39    void broadcast();40private:41    pthread_cond_t mCond;42};4344inline Condition::Condition() {45    pthread_cond_init(&mCond, NULL);46}4748inline Condition::Condition(int type) {49    if (type == SHARED) {50        pthread_condattr_t attr;51        pthread_condattr_init(&attr);52        pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);53        pthread_cond_init(&mCond, &attr);54        pthread_condattr_destroy(&attr);55    } else {56        pthread_cond_init(&mCond, NULL);57    }58}5960inline Condition::~Condition() {61    pthread_cond_destroy(&mCond);62}6364inline status_t Condition::wait(Mutex &mutex) {65    return -pthread_cond_wait(&mCond, &mutex.mMutex);66}6768inline status_t Condition::waitRelative(Mutex &mutex, nsecs_t reltime) {69    struct timeval t;70    struct timespec ts;71    gettimeofday(&t, NULL);72    ts.tv_sec  = t.tv_sec;73    ts.tv_nsec = t.tv_usec*1000;7475    ts.tv_sec  += reltime / 1000000000;76    ts.tv_nsec += reltime % 1000000000;77    if (ts.tv_nsec >= 1000000000) {78        ts.tv_nsec -= 1000000000;79        ts.tv_sec  += 1;80    }81    return -pthread_cond_timedwait(&mCond, &mutex.mMutex, &ts);82}8384inline void Condition::signal() {85    pthread_cond_signal(&mCond);86}8788inline void Condition::broadcast() {89    pthread_cond_broadcast(&mCond);90}9192#endif //CONDITION_H

Thread封装

为了方便使用线程,我们对 pthread 进行封装。完整的代码如下:

 1#include <Mutex.h> 2#include <Condition.h> 3 4typedef enum { 5    Priority_Default = -1, 6    Priority_Low = 0, 7    Priority_Normal = 1, 8    Priority_High = 2 9} ThreadPriority; 10 11class Runnable { 12public: 13    virtual ~Runnable(){} 14 15    virtual void run() = 0; 16}; 17 18/** 19 * Thread can use a custom Runnable, but must delete Runnable constructor yourself 20 */ 21class Thread : public Runnable { 22public: 23 24    Thread(); 25 26    Thread(ThreadPriority priority); 27 28    Thread(Runnable *runnable); 29 30    Thread(Runnable *runnable, ThreadPriority priority); 31 32    virtual ~Thread(); 33 34    void start(); 35 36    void join(); 37 38    void detach(); 39 40    pthread_t getId() const; 41 42    bool isActive() const; 43 44protected: 45    static void *threadEntry(void *arg); 46 47    int schedPriority(ThreadPriority priority); 48 49    virtual void run(); 50 51protected: 52    Mutex mMutex; 53    Condition mCondition; 54    Runnable *mRunnable; 55    ThreadPriority mPriority; // thread priority 56    pthread_t mId;  // thread id 57    bool mRunning;  // thread running 58    bool mNeedJoin; // if call detach function, then do not call join function 59}; 60 61inline Thread::Thread() { 62    mNeedJoin = true; 63    mRunning = false; 64    mId = -1; 65    mRunnable = NULL; 66    mPriority = Priority_Default; 67} 68 69inline Thread::Thread(ThreadPriority priority) { 70    mNeedJoin = true; 71    mRunning = false; 72    mId = -1; 73    mRunnable = NULL; 74    mPriority = priority; 75} 76 77inline Thread::Thread(Runnable *runnable) { 78    mNeedJoin = false; 79    mRunning = false; 80    mId = -1; 81    mRunnable = runnable; 82    mPriority = Priority_Default; 83} 84 85inline Thread::Thread(Runnable *runnable, ThreadPriority priority) { 86    mNeedJoin = false; 87    mRunning = false; 88    mId = -1; 89    mRunnable = runnable; 90    mPriority = priority; 91} 92 93inline Thread::~Thread() { 94    join(); 95    mRunnable = NULL; 96} 97 98inline void Thread::start() { 99    if (!mRunning) {100        pthread_create(&mId, NULL, threadEntry, this);101        mNeedJoin = true;102    }103    // wait thread to run104    mMutex.lock();105    while (!mRunning) {106        mCondition.wait(mMutex);107    }108    mMutex.unlock();109}110111inline void Thread::join() {112    Mutex::Autolock lock(mMutex);113    if (mId > 0 && mNeedJoin) {114        pthread_join(mId, NULL);115        mNeedJoin = false;116        mId = -1;117    }118}119120inline  void Thread::detach() {121    Mutex::Autolock lock(mMutex);122    if (mId >= 0) {123        pthread_detach(mId);124        mNeedJoin = false;125    }126}127128inline pthread_t Thread::getId() const {129    return mId;130}131132inline bool Thread::isActive() const {133    return mRunning;134}135136inline void* Thread::threadEntry(void *arg) {137    Thread *thread = (Thread *) arg;138139    if (thread != NULL) {140        thread->mMutex.lock();141        thread->mRunning = true;142        thread->mCondition.signal();143        thread->mMutex.unlock();144145        thread->schedPriority(thread->mPriority);146147        // when runnable is exit,run runnable else run()148        if (thread->mRunnable) {149            thread->mRunnable->run();150        } else {151            thread->run();152        }153154        thread->mMutex.lock();155        thread->mRunning = false;156        thread->mCondition.signal();157        thread->mMutex.unlock();158    }159160    pthread_exit(NULL);161162    return NULL;163}164165inline int Thread::schedPriority(ThreadPriority priority) {166    if (priority == Priority_Default) {167        return 0;168    }169170    struct sched_param sched;171    int policy;172    pthread_t thread = pthread_self();173    if (pthread_getschedparam(thread, &policy, &sched) < 0) {174        return -1;175    }176    if (priority == Priority_Low) {177        sched.sched_priority = sched_get_priority_min(policy);178    } else if (priority == Priority_High) {179        sched.sched_priority = sched_get_priority_max(policy);180    } else {181        int min_priority = sched_get_priority_min(policy);182        int max_priority = sched_get_priority_max(policy);183        sched.sched_priority = (min_priority + (max_priority - min_priority) / 2);184    }185186    if (pthread_setschedparam(thread, policy, &sched) < 0) {187        return -1;188    }189    return 0;190}191192inline void Thread::run() {193    // do nothing194}

备注:

  • 为何不用C++11的线程?

编译器可能不支持C++11。

这里只是做兼容,而且音视频的库基本都是C语言编写的,这里主要是考虑到二进制接口兼容性的问题。在使用带异常的C++时,有可能会导致ffmpeg某些版本出现偶然的内部崩溃问题,这个是我在实际使用过程中发现的。

这个C++二进制接口兼容性问题各个技术大牛有专门讨论过,我并不擅长C++,也讲不出更深入的说法,想要了解的话,建议自行找资料了解,这里就不费口舌了。

  • 当继承Thread类时,我们需要重写run方法。

Runnable 是一个抽象基类,用来模仿Java层的Runnable接口。当我们使用Runnable时,必须有外部释放Runnable的内存,这里并没有垃圾回收功能,要做成Java那样能够自动回收内存,这个超出了我的能力范围。我这里只是为了方便使用而简单地将pthread封装起来使用而已。

如果要使用pthread_detach的时候,希望调用Thread的detach方法。这样Thread的线程标志不会混乱。

调用pthread_detach后,如果不调用pthread_exit方法,会导致线程结构有个8K的内存没有释放掉。默认情况下是没有detach的,此时,如果要释放线程的内存,需要在线程执行完成之后,不管是否调用了pthread_exit方法,都调用pthread_join方法阻塞销毁线程占用的那个8K内存。

这也是我为何要将Thread封装起来的原因之一。我们有时候不想detach一个线程,这时候,我们就需要用join来释放,重复调用的话,会导致出现 fatal signal 6的情况。

备注2

关于NDK 常见的出错信息意义:

  • fatal signal 4:常见情况是方法没有返回值,比如一个返回int的方法,到最后没有return ret。

  • fatal signal 6:常见情况是pthread 线程类阻塞了,比如重复调用join方法,或者一个线程detach之后,然后又调用join就会出现这种情况

  • fatal signal 11:空指针出错。在多线程环境下,由于对象在另外一个线程释放调用,而该线程并没有停止,仍然在运行阶段,此时调用到该被释放的对象时,就会出现fatalsignal 11 的错误。

  • 其他的出错信息一般比较少见,至少本人接触到的NDK代码,还没遇到过其他出错信息。

好了,我们这里封装完了基础公共类之后,就可以愉快地编写C/C++代码了。

关注微信公众号【 纸上浅谈】,阅读更多Android开发、音视频、Camera、OpenGL、NDK 开发相关文章~~~

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1

联系我们