ffmpeg实时编码解码部分代码

Posted cynchanpin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ffmpeg实时编码解码部分代码相关的知识,希望对你有一定的参考价值。

程序分为编码端和解码端,两端通过tcp  socket通信,编码端一边编码一边将编码后的数据发送给解码端。解码端一边接收数据一边将解码得到的帧显示出来。

代码中的编码端编码的是实时屏幕截图。

代码调用了Qt SDK。

#ifndef MAPTHREAD_H
#define MAPTHREAD_H

#include <QThread>
#include <QTcpSocket>
#include <QTimer>
#include <QColor>
#include <QImage>
#include <QPixmap>
#include <QTime>
#include <QDateTime>

#include <stdio.h>

struct AVFrame;
struct AVPacket;
struct AVCodec;
struct AVCodecContext;


class MapThread : public QThread
{
    Q_OBJECT

private:
#ifdef DEBUG
    FILE* log;
    QTime* time;
    QDateTime dt;
#endif

    AVFrame *frame;
    AVPacket* pkt;
    AVCodec *codec;
    AVCodecContext *c;
    int i, ret, x, y, got_output;

    int dest_width;           //client指定的宽度
    int dest_height;          //client指定的高度
    int send_width;           //发送图像的宽度
    int send_height;          //发送图像的高度
    int scaleby;              //缩放是根据目的图像的高度还是宽度

    uchar* sent_img_buf;   //buffer of the image that have been sent
    uchar* curt_img_buf;   //buffer of the current image
    uchar* send_data_buf;
    uchar cmd_buf[4];
    int   cmd_buf_fill;

    bool started;
    bool inited;

    int          interval;         //帧间时间间隔
    QTcpSocket*  mapSocket;
    QTimer* timer;

public:
    MapThread(QTcpSocket* socket, QObject *parent = 0);
    ~MapThread();

    const static int SCALE_BY_WIDTH  = 1;
    const static int SCALE_BY_HEIGHT = 2;

    void setSendInterval(int i);
    
signals:
    
public slots:
    void sendFrame();
    void newData();
    void newCommand();
    void quit();

protected:
    void run();
    
};

#endif // MAPTHREAD_H


void MapThread::sendFrame()
{
    if(!started)
        return;

    if(!inited)
    {
        avcodec_register_all();

        c= NULL;
        pkt = new AVPacket;
        i = 0;

#ifdef DEBUG
        fprintf(log, "Encode video file %s\n", "test.mpg");
        fflush(log);
#endif
        /* find the mpeg1 video encoder */
        codec = avcodec_find_encoder(AV_CODEC_ID_MPEG1VIDEO);

        if (codec == 0)
        {
#ifdef DEBUG
            fprintf(log, "Codec not found\n");
            fflush(log);
#endif
            exit(1);
        }

        c = avcodec_alloc_context3(codec);
        if (!c)
        {
#ifdef DEBUG
            fprintf(log, "Could not allocate video codec context\n");
            fflush(log);
#endif
            exit(1);
        }
        //c->bit_rate = 400000;
        c->width = dest_width;
        c->height = dest_height;

        c->time_base = (AVRational){1,25};
        c->gop_size = 100;
        c->max_b_frames = 0;
        c->delay = 0;
        c->pix_fmt = AV_PIX_FMT_YUV420P;

        //av_opt_set(c->priv_data, "preset", "slow", 0);

        av_opt_set(c->priv_data, "preset", "superfast", 0);
        av_opt_set(c->priv_data, "tune", "zerolatency", 0);

        int re = avcodec_open2(c, codec, NULL);
        av_opt_set(c->priv_data, "tune", "zerolatency", 0);
        if (re < 0) {
#ifdef DEBUG
            fprintf(log, "Could not open codec:%d\n", re);
            fflush(log);
#endif
            exit(1);
        }

        frame = av_frame_alloc();
        if (!frame) {
#ifdef DEBUG
            fprintf(log, "Could not allocate video frame\n");
            fflush(log);
#endif
            exit(1);
        }
        frame->format = c->pix_fmt;
        frame->width  = c->width;
        frame->height = c->height;

        ret = av_image_alloc(frame->data, frame->linesize, c->width, c->height, c->pix_fmt, 32);
        if (ret < 0) {
#ifdef DEBUG
            fprintf(log, "Could not allocate raw picture buffer\n");
            fflush(log);
#endif
            exit(1);
        }
        inited = true;
    }


    if(mapSocket == 0)
    {
#ifdef DEBUG
        qDebug()<<"null socket"<<endl;
#endif
        return;
    }
    else if(mapSocket->isOpen() == false)
    {
        return;
    }
    else if(mapSocket->isWritable() == false)
    {
        return;
    }
#ifdef DEBUG
    fprintf(log, "start cap:%d\n", QDateTime::currentDateTime().msecsTo(dt));
#endif
    QImage image = Interface::grapScreen().toImage();
    image = image.scaled(QSize(dest_width, dest_height));

#ifdef DEBUG
    fprintf(log, "end cap:%d\n", QDateTime::currentDateTime().msecsTo(dt));
    //fprintf(log, "cap:%d\n", time->elapsed());
    fflush(log);
#endif


    av_init_packet(pkt);
    pkt->data = NULL;    // packet data will be allocated by the encoder
    pkt->size = 1000000;

    for (int h = 0; h < c->height; h++)
    {
        for (int w = 0; w < c->width; w++)
        {
            QRgb rgb = image.pixel(w, h);

            int r = qRed(rgb);
            int g = qGreen(rgb);
            int b = qBlue(rgb);

            int dy = ((66*r + 129*g + 25*b) >> 8) + 16;
            int du = ((-38*r + -74*g + 112*b) >> 8) + 128;
            int dv = ((112*r + -94*g + -18*b) >> 8) + 128;

            uchar yy = (uchar)dy;
            uchar uu = (uchar)du;
            uchar vv = (uchar)dv;

            frame->data[0][h * frame->linesize[0] + w] = yy;

            if(h % 2 == 0 && w % 2 == 0)
            {
                frame->data[1][h/2 * (frame->linesize[1]) + w/2] = uu;
                frame->data[2][h/2 * (frame->linesize[2]) + w/2] = vv;
            }

        }
    }

    frame->pts = i;

    ret = avcodec_encode_video2(c, pkt, frame, &got_output);

    if (ret < 0)
    {
#ifdef DEBUG
        fprintf(log, "Error encoding frame\n");
        fflush(log);
#endif
        exit(1);
    }

    if (got_output)
    {
#ifdef DEBUG
        fprintf(log, "start send:%d\n", QDateTime::currentDateTime().msecsTo(dt));
#endif
        int ss = pkt->size;
#ifdef DEBUG
        qDebug()<<"ss:"<<ss;
        fprintf(log, "size:%d\n", ss);
#endif
        writeAndBlock(mapSocket, pkt->data, ss);
        mapSocket->flush();
#ifdef DEBUG
        fprintf(log, "end send:%d\n", QDateTime::currentDateTime().msecsTo(dt));
#endif

        av_free_packet(pkt);
    }

    i ++;
}


解码端:

#ifndef MAPTHREAD_H
#define MAPTHREAD_H

#include <QThread>
#include <QTcpSocket>
#include <QDebug>
#include <QImage>
#include <QTime>

#include "version.h"
struct AVFrame;
struct AVPacket;
struct AVCodec;
struct AVCodecContext;

#define INBUF_SIZE 4096000
#define AUDIO_INBUF_SIZE 20480
#define AUDIO_REFILL_THRESH 4096
#define QIMAGE_BUFFER_SIZE 12000000

class MapThread : public QThread
{
    Q_OBJECT

private:
    QTime* time;
    int newF;

    uchar* img_buf;
    QTcpSocket* mapSocket;
    QString     address;
    int         port;
    bool socketConnected;

    int request_width;
    int request_height;

    uchar* recv_buf;
    uchar* frame_buf;
    int    frame_buf_fill;
    int    frame_bytes;
    uchar  cmd_buf[8];
    int    cmd_buf_fill;

    bool cmd_got;
    bool frame_size_setted;

    bool cmd_parsed;
    int subX;
    int subY;
    int subWidth;
    int subHeight;
    int subSize;
    int subFill;

    bool inited;
    AVFrame *frame;
    AVPacket* pkt;
    AVCodec *codec;
    AVCodecContext *c;
    int i, ret, x, y, got_output;

    //QImage* image;

    uint8_t* inbuf;
#ifdef DEBUG
    FILE* log;
    int readlen;
#endif

    int decode_write_frame(AVCodecContext *avctx, AVFrame *frame, AVPacket *pkt);

public:
    int received_frame_width;
    int received_frame_height;
    QImage*  image;

    MapThread(QString add, int p, int w, int h, QObject *parent = 0);
    void sendRequestSize(int width, int height);
    void getSubWindow();
    void parseCommand();
    void updateFrame();
    
signals:
    void frameGot(QImage*);
    void frameSizeChanged(int, int);
    
public slots:
    void newData();
    void hostConnected();

protected:
    void run();
    
};

#endif // MAPTHREAD_H


int MapThread::decode_write_frame(AVCodecContext *avctx, AVFrame *frame, AVPacket *pkt)
{
    int len, got_frame;
    len = avcodec_decode_video2(avctx, frame, &got_frame, pkt);

    if (got_frame)
    {
#ifdef DEBUG
        fprintf(tlog, "get frame:%d\n", QDateTime::currentDateTime().msecsTo(dt));
        //fprintf(tlog, "got frame:%d\n", ttime->elapsed());
#endif

        if(image == 0)
            image = new QImage(img_buf, avctx->width, avctx->height, QImage::Format_RGB888);

        received_frame_width = avctx->width;
        received_frame_height = avctx->height;

        for(int h = 0; h < avctx->height; h++)
        {
            for(int w = 0; w < avctx->width; w ++)
            {
                int hh = h >> 1;
                int ww = w >> 1;
                int Y = frame->data[0][h * frame->linesize[0] + w];
                int U = frame->data[1][hh * (frame->linesize[1]) + ww];
                int V = frame->data[2][hh * (frame->linesize[2]) + ww];

                int C = Y - 16;
                int D = U - 128;
                int E = V - 128;

                int r = 298 * C           + 409 * E + 128;
                int g = 298 * C - 100 * D - 208 * E + 128;
                int b = 298 * C + 516 * D           + 128;

                r = qBound(0, r >> 8, 255);
                g = qBound(0, g >> 8, 255);
                b = qBound(0, b >> 8, 255);

                QRgb rgb = qRgb(r, g, b);
                image->setPixel(QPoint(w, h), rgb);
            }
        }
#ifdef DEBUG
        fprintf(tlog, "emit frame:%d\n", QDateTime::currentDateTime().msecsTo(dt));
        //fprintf(tlog, "emit frame:%d\n", ttime->elapsed());
#endif
        emit frameGot(image);
    }
    if (pkt->data) {
        pkt->size -= len;
        pkt->data += len;
    }

    return 0;
}


void MapThread::newData()
{
    if(!inited)
    {
        avcodec_register_all();

        pkt = new AVPacket;
        av_init_packet(pkt);

        memset(inbuf + INBUF_SIZE, 0, FF_INPUT_BUFFER_PADDING_SIZE);

        codec = avcodec_find_decoder(AV_CODEC_ID_MPEG1VIDEO);
        if (!codec)
        {
#ifdef DEBUG
            fprintf(log, "Codec not found\n");
            fflush(log);
#endif
            exit(1);
        }
        c = avcodec_alloc_context3(codec);

        if (!c) {
#ifdef DEBUG
            fprintf(log, "Could not allocate video codec context\n");
            fflush(log);
#endif
            exit(1);
        }

        av_opt_set(c->priv_data, "preset", "superfast", 0);
        av_opt_set(c->priv_data, "tune", "zerolatency", 0);

        c->delay = 0;

        if(codec->capabilities&CODEC_CAP_TRUNCATED)
            c->flags|= CODEC_FLAG_TRUNCATED;
        if (avcodec_open2(c, codec, NULL) < 0) {
#ifdef DEBUG
            fprintf(log, "Could not open codec\n");
            fflush(log);
#endif
            exit(1);
        }

        frame = av_frame_alloc();
        if (!frame) {
#ifdef DEBUG
            fprintf(log, "Could not allocate video frame\n");
            fflush(log);
#endif
            exit(1);
        }

        inited = true;
    }
    while(true)
    {
        int nread = mapSocket->read((char*)inbuf, INBUF_SIZE);
#ifdef DEBUG
        readlen += nread;
        fprintf(tlog, "recv time:%d\n", QDateTime::currentDateTime().msecsTo(dt));
        fprintf(tlog, "recv all:%d\n", readlen);
        fflush(tlog);
#endif

        if(nread <= 0)
            break;

        av_init_packet(pkt);
        pkt->size = nread;
        pkt->data = inbuf;
        while (pkt->size > 0)
        {
            if (decode_write_frame(c, frame, pkt) < 0)
                exit(1);
        }
        av_free_packet(pkt);
    }
}



以上是关于ffmpeg实时编码解码部分代码的主要内容,如果未能解决你的问题,请参考以下文章

实时解码 - 标准输入到标准输出

FFmpeg源代码结构图 - 解码

FFMpeg.AutoGen讲解官方example代码:解码(未完成)

没有解码/编码的FFmpeg remux

Qt与FFmpeg联合开发指南——编码:代码流程演示

ffmpeg编解码器包装类