ffplay 内存优化

上一篇文章:https://blog.csdn.net/yinsui1839/article/details/127643856?spm=1001.2014.3001.5502

这两天一直阅读ffplay.c,发现AvPackct在发送给解码器之前总会调用av_packet_alloc 进行申请内存存放AvPackt然后用完后再av_packet_free()。那为什么不一次申请够,循环利用,再到程序结尾再释放,所以就有了这次改动

StreamDataPool.h 可以看我前一篇博客

//
// Created by 19321 on 2022/8/20.
//

#ifndef MY_APPLICATION_STREAMDATAPOOL_H
#define MY_APPLICATION_STREAMDATAPOOL_H

#include <iostream>
#include <list>
#include <cstring>
extern "C" {
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/eval.h"
#include "libavutil/mathematics.h"
#include "libavutil/pixdesc.h"
#include "libavutil/imgutils.h"
#include "libavutil/dict.h"
#include "libavutil/fifo.h"
#include "libavutil/parseutils.h"
#include "libavutil/samplefmt.h"
#include "libavutil/time.h"
#include "libavutil/bprint.h"
#include "libavformat/avformat.h"
#include "libavdevice/avdevice.h"
#include "libswscale/swscale.h"
#include "libavutil/opt.h"
#include "libavcodec/avfft.h"
#include "libswresample/swresample.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libswscale/version.h"
#include "libswresample/swresample.h"
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/display.h"
#include "libavutil/mathematics.h"
#include "libavutil/imgutils.h"
#include "libavutil/parseutils.h"
#include "libavutil/eval.h"
#include "libavutil/dict.h"
#include "libavutil/opt.h"
#include "libavcodec/avcodec.h"
#include "libavfilter/avfilter.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"

#include <SDL.h>
#include <SDL_thread.h>

}

#include "StreamDataPool.h"
using namespace std;

#define MAX_CAMERA_BUFFER_SIZE 1000
#define WIGHT 640
#define HEIGHT 480
#define IMAGE_SIZE (WIGHT*HEIGHT)

#include <iostream>
#include <list>
#include "StreamDataPool.h"
#include <functional>

using namespace std;

template<class T>
class StreamDataPool {
public:
    T *buf = NULL;
    int mSize;
    int mWriteOps = 0;
    int mReadOps = 0;
    bool isVaild = false;
    int nb_packets;
    int size;
    int64_t duration;
    int abort_request;
    int serial;
    SDL_mutex *mutex;
    SDL_cond *cond;
    string mName;

    bool writeData(T *data) {
        if (buf == NULL) return false;
        if (((mWriteOps + 1) % mSize) == mReadOps) {
            printf("writeOps false %d %d \n", mWriteOps, mReadOps);
            return false;
        }
        buf[mWriteOps] = *data;
        mWriteOps = (mWriteOps + 1) % mSize;

        return true;
    }

    T *readData() {
        if (buf == NULL) return NULL;
        if (mReadOps == mWriteOps) {
            printf("readData false %d %d \n", mWriteOps, mReadOps);
            return NULL;
        }
        int readOps = mReadOps;
        mReadOps = (mReadOps + 1) % mSize;
        return &buf[readOps];
    }

    list<void *> ReadBufferList(bool isOnlyRead) {
        list<void *> dataList;
        if (buf == NULL) return dataList;
        int readOps = mReadOps;

        while (readOps != mWriteOps) {
            dataList.push_back(&buf[readOps]);
            readOps = (readOps + 1) % mSize;
        }

        if (!isOnlyRead) {
            mReadOps = readOps;
        }

        return dataList;
    }

    void pop() {
        if (mReadOps == mWriteOps) return;
        mReadOps = (mReadOps + 1) % mSize;
    }

    void setSize(int size) {
        mSize = size;
        mWriteOps = 0;
        mReadOps = 0;
        buf = new T[size];
    }

    void setSize(int size, string name) {
        mSize = size;
        mWriteOps = 0;
        mReadOps = 0;
        serial = 0;
        buf = new T[size];
        mName = name;
    }

    void destroy() {
        isVaild = false;
        if (buf != NULL) {
            delete buf;
            buf = NULL;
        }
    }

    bool isFull(){
       if(mWriteOps - mReadOps == mSize) return true;
    }

    bool isEnty(){
        if(mReadOps - mWriteOps) return true;
        return false; 
    }

    void clean(){
        mWriteOps = 0;
        mReadOps = 0;
    }
};

class PktNode{
    public:
    int index = 0;
    int64_t pts = 0;
    int type = 0;
    PktNode(){
    };
    PktNode(int Index ,int64_t Pts,int Type ){
        this->index=Index;
        this->pts=Pts;
        this->type=Type;
    };
    PktNode &operator=(PktNode &data) {
        this->index=data.index;
        this->pts=data.pts;
        this->type=data.type;
        return *this;
    }
};

class PktPool {
    public:
    AVPacket *pkt = NULL;
    int serial=0;
    int index = 0;
    static int curIndex;
    PktPool() {
        pkt = av_packet_alloc();
    };
    
    ~PktPool(){
        av_packet_free(&pkt);
    }
    PktPool &operator=(AVPacket *data) {
        if(pkt != NULL){
            av_packet_move_ref(this->pkt, data);
        }
        return *this;
    }
    
    PktPool &operator=(PktPool &data) {
        av_packet_move_ref(this->pkt, data.pkt);
        return *this;
    }
    void free(){
        av_packet_unref(this->pkt);
    }
    static PktPool *PktPoolPtr;
  
    static PktPool *GetPktPool() {
        curIndex = (curIndex + 1) % MAX_CAMERA_BUFFER_SIZE;
        PktPoolPtr[curIndex].index = curIndex;
        return &PktPoolPtr[curIndex];
    }
};

PktPool* PktPool::PktPoolPtr = new PktPool[MAX_CAMERA_BUFFER_SIZE];
int PktPool::curIndex = 0;

#endif //MY_APPLICATION_STREAMDATAPOOL_H

ffplay.cpp

/*
*主要修改了PacketQueue *q 改为 StreamDataPool<PktNode> *q
*优化代码每一次packet_queue_put_private都会调用av_fifo_write,解码的时候还要调用*av_fifo_read获取,减少FFmepeg API使用
*/
static int packet_queue_put_private(StreamDataPool<PktNode> *q, PktPool *pktPool)
{
    bool ret;

    if (q->abort_request)
       return -1;

    PktNode pktNode(pktPool->index, pktPool->pkt->pts, pktPool->pkt->stream_index);
    ret = q->writeData(&pktNode);
    if (ret == false)
        return -1;
    q->nb_packets++;
    q->size += pktPool->pkt->size;
    q->duration += pktPool->pkt->duration;
    /* XXX: should duplicate packet data in DV case */
    SDL_CondSignal(q->cond);
    return 0;
}

static int packet_queue_put(StreamDataPool<PktNode> *q, AVPacket *pkt)
{
    int ret;
    PktPool *pktPool = PktPool::GetPktPool();//在内存池里获取到对应的pktPool容器

    if (pktPool == NULL)
    {
        av_packet_unref(pkt);
        return -1;
    }
   
    SDL_LockMutex(q->mutex);
    *pktPool = pkt;  //赋值
    pktPool->serial = q->serial;

    packet_queue_put_private(q,pktPool);
    SDL_UnlockMutex(q->mutex);

    return ret;
}

/* return < 0 if aborted, 0 if no packet and > 0 if packet.  g++ fftools/ffplay.cpp -I /usr/local/include -I /usr/include/SDL2/ -L /usr/local/lib  -lavformat -lavcodec -lavutil -lswscale -lSDL2  -lavformat -lavcodec -lavdevice -lavutil -lz -lm -lswresample -lpostproc*/

static int packet_queue_get(StreamDataPool<PktNode> *q, AVPacket *pkt, int block, int *serial)
{
    int ret;

    SDL_LockMutex(q->mutex);

    for (;;)
    {
        if (q->abort_request)
        {
            ret = -1;
            break;
        }

        PktNode *pktNode = q->readData();//从数据队列获取对应的节点
        PktPool *pktPool = NULL;
        if (pktNode != NULL)
        {
            pktPool = &PktPool::PktPoolPtr[pktNode->index];//获取对应的数据内存段
        }

        if (pktPool != NULL)
        {
            q->nb_packets--;
            q->size -= pktPool->pkt->size;
            q->duration -= pktPool->pkt->duration;
            av_packet_move_ref(pkt, pktPool->pkt);
            if (serial)
                *serial = pktPool->serial;

            ret = 1;
            break;
        }
        else if (!block)
        {
            ret = 0;
            break;
        }
        else
        {
            SDL_CondWait(q->cond, q->mutex);
        }
    }
   // printf("ret = %d",ret);
    SDL_UnlockMutex(q->mutex);
    return ret;
}




原版:

/*
*原版
*优化代码每一次packet_queue_put_private都会调用av_fifo_write,解码的时候还要调用*av_fifo_read获取,减少FFmepeg API使用
*/
static int packet_queue_put_private(PacketQueue *q, AVPacket *pkt)
{
    MyAVPacketList pkt1;
    int ret;

    if (q->abort_request)
       return -1;


    pkt1.pkt = pkt;
    pkt1.serial = q->serial;

    ret = av_fifo_write(q->pkt_list, &pkt1, 1);
    if (ret < 0)
        return ret;
    q->nb_packets++;
    q->size += pkt1.pkt->size + sizeof(pkt1);
    q->duration += pkt1.pkt->duration;
    /* XXX: should duplicate packet data in DV case */
    SDL_CondSignal( q->cond);
    return 0;
}

static int packet_queue_put(PacketQueue *q, AVPacket *pkt)
{
    AVPacket *pkt1;
    int ret;

    pkt1 = av_packet_alloc();
    if (!pkt1) {
        av_packet_unref(pkt);
        return -1;
    }
    av_packet_move_ref(pkt1, pkt);

    SDL_LockMutex(q->mutex);
    ret = packet_queue_put_private(q, pkt1);
    SDL_UnlockMutex(q->mutex);

    if (ret < 0)
        av_packet_free(&pkt1);

    return ret;
}

static int packet_queue_put_nullpacket(PacketQueue *q, AVPacket *pkt, int stream_index)
{
    pkt->stream_index = stream_index;
    return packet_queue_put(q, pkt);
}

/* packet queue handling */
static int packet_queue_init(PacketQueue *q)
{
    memset(q, 0, sizeof(PacketQueue));
    q->pkt_list = av_fifo_alloc2(1, sizeof(MyAVPacketList), AV_FIFO_FLAG_AUTO_GROW);
    if (!q->pkt_list)
        return AVERROR(ENOMEM);
    q->mutex = SDL_CreateMutex();
    if (!q->mutex) {
        av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());
        return AVERROR(ENOMEM);
    }
    q->cond = SDL_CreateCond();
    if (!q->cond) {
        av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
        return AVERROR(ENOMEM);
    }
    q->abort_request = 1;
    return 0;
}

ffplay.cpp 主要替换PacketQueue *q 为StreamDataPool<PktNode> *q,剔除av_fifo_write av_fifo_read 改为从数据池里获取

编译:g++ ffplay.cpp StreamDataPool.cpp StreamDataPool.h -I /usr/local/include -I /usr/include/SDL2/ -L /usr/local/lib  -lavformat -lavcodec -lavutil -lswscale -lSDL2  -lavformat -lavcodec -lavdevice -lavutil -lz -lm -lswresample -lpostproc

编译后可正常播放视频https://download.csdn.net/download/yinsui1839/86876644

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hmbbPdx_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值