在上一篇文章,我们通过投屏软件的流程图对软件做了一个整体的了解和分析,并且编写了adb相关操作的代码,这些代码完成了adb的启动、push Android设备端程序、reverse设置端口转发、shell app_process 执行Android设备端程序、PC端listen端口接收socket连接。
PC端和Android设备端将会建立3条socket链路,分别是video socket、audio socket、control socket,这篇文章我们主要学习video socket的建立及后续处理流程。
下图是视频处理流程
我们逐步分析下流程:
- socket建立后首先读取4字节的codec_id,通过codec_id的值判断视频编码类型,获取AVCodec,创建AVCodecContext
- 读取视频流的width、height,一共8个字节,每个值分别是4个字节。用读取到的值设置AVCodecContext的width、height属性,并且设置pix_fmt = AV_PIX_FMT_YUV420P。
- 从socket链路中读取AVPacket
- 将读取到的AVPacket数据交给sinks处理,这里设置了2中类型的sink:decoder sink用于解码AVPacket,转换成yuv格式后交给opengl绘图,显示设备屏幕;recoder sink将AVPacket数据放入了队列,如果录制线程启动会读取队列中的数据保存成文件。可以根据需要创建更多的sink,例如可以创建一个推流的sink用于直播,只需要继承PacketSink这个基类编写推流相关的逻辑。
- 循环读取AVPacket,回到第3步。
在视频解码流程中我们引入了sink这个概念,并且对它进行了抽象,通过此设计可以灵活的实现视频数据包的个性化处理。
下面是视频解码流程部分相关的代码
#ifndef PACKET_SINK_H
#define PACKET_SINK_H
extern "C"{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
#include <mutex>
#include <condition_variable>
#include <thread>
#include <vector>
#include <QObject>
#include "frame_buffer.h"
#include <functional>
struct PacketMerger {
uint8_t *config;
size_t configSize;
};
class FrameSink:public QObject{
Q_OBJECT
public:
explicit FrameSink(QObject *parent = nullptr);
virtual bool open(AVCodecContext *ctx) = 0;
virtual void close() = 0;
virtual bool push(AVFrame *frame) = 0;
};
class ScreenSink:public FrameSink
{
Q_OBJECT
public:
explicit ScreenSink(QObject *parent = nullptr);
~ScreenSink();
virtual bool open(AVCodecContext *ctx);
virtual void close();
virtual bool push(AVFrame *frame);
FrameBuffer *getFrameBuffer();
signals:
void screenInitSize(int w,int h);
void newFrame();
private:
FrameBuffer *frameBuffer;
};
typedef ScreenSink PreviewSink;
class PacketSink
{
public:
PacketSink();
~PacketSink();
virtual bool open(AVCodecContext *ctx) = 0;
virtual void close() = 0;
virtual bool push(AVPacket *packet) = 0;
//virtual void disable() = 0;
};
class DecoderSink: public PacketSink{
public:
DecoderSink();
~DecoderSink();
void addFrameSink(FrameSink *sink);
virtual bool open(AVCodecContext *ctx);
virtual void close();
virtual bool push(AVPacket *packet);
//virtual void disable();
private:
AVCodecConte