FFmpeg安卓流媒体播放项目02:实现FFDemux的Open函数打开媒体文件以及Read函数读取帧数据接口编写

FFDemux的Open实现打开媒体文件

FFdemux的Read读取帧数据接口编写

项目地址:https://github.com/popobo/BoPlayer

主要是增加了三个类XData, IDemux, FFDemux, 具体实现和作用如下

数据类:XData

  • 该类用于存储各个线程生产的需要交互的数据
  • unsigned char *data 存储的数据的地址
  • int size 存储的数据的大小
  • void Drop() 释放资源
//XData.h
#ifndef BOPLAY_XDATA_H
#define BOPLAY_XDATA_H

struct XData {
    unsigned char *data = 0;
    int size = 0;
    void Drop();
};

#endif //BOPLAY_XDATA_H
//XData.cpp
#include "XData.h"
extern "C"{
#include "libavformat/avformat.h"
}

void XData::Drop() {
    if(!data){
        return;
    }
    av_packet_free((AVPacket **)&data);
    data = 0;
    size = 0;
}

解封装接口类:IDemux,即适配器模式的目标抽象类,定义了客户所需的端口

  • virtual bool Open(const char *url):接口打开文件或者流媒体链接,并进行解封装,获取音视频基本信息(如封装格式、编码格式和视频时长等等),传入参数为本地文件地址或流媒体url, 打开成功返回true,失败则返回false
  • virtual XData Read():该接口负责读取一帧的数据并返回出来,主要是将
//IDemux.h
#ifndef BOPLAY_IDEMUX_H
#define BOPLAY_IDEMUX_H

#include "XData.h"

//解封装接口类
class IDemux {
public:
    //打开文件或者流媒体 rtmp http rtsp
    virtual bool Open(const char *url) = 0;

    //读取一帧数据,数据由调用者清理
    virtual XData Read() = 0;

    //总时长(单位ms)
    int totalMs = 0;
};

#endif //BOPLAY_IDEMUX_H
//IDemux.cpp
#include "IDemux.h"

解封装的实现类:FFDemux,即适配器模式中的适配器类,适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系,FFDemux调用了ffmpeg

//FFDemux.h
#ifndef BOPLAY_FFDEMUX_H
#define BOPLAY_FFDEMUX_H

#include "IDemux.h"
//声明, 不引用头文件, 封装后调用者不需要知道ffmpeg的头文件
struct AVFormatContext;

class FFDemux: public IDemux{
public:
    //打开文件或者流媒体 rtmp http rtsp
    virtual bool Open(const char *url);

    //读取一帧数据,数据由调用者清理
    virtual XData Read();

    FFDemux();

private:
    //只有在无参数构造函数时, 这个赋值才会生效
    //AVFormatContext描述了一个媒体文件或媒体流的构成和基本信息
    AVFormatContext *ic = 0;
};

#endif //BOPLAY_FFDEMUX_H
//FFDemux.cpp
#include "FFDemux.h"
#include "XLog.h"
extern "C"{
#include "libavformat/avformat.h"
}
bool FFDemux::Open(const char *url) {
    XLOGI("open url %s begin", url);
    int re = avformat_open_input(&ic, url, 0, 0);
    if (re != 0){
        char buf[1024] = {0};
        av_strerror(re, buf, sizeof(buf));
        XLOGE("FFDemux open %s failed!", url);
        return false;
    }
    XLOGI("FFDemux open %s successfully!", url);

    //读取文件信息
    re = avformat_find_stream_info(ic, 0);
    if (re != 0){
        char buf[1024] = {0};
        av_strerror(re, buf, sizeof(buf));
        XLOGE("FFDemux avformat_find_stream_info %s failed!", url);
        return false;
    }
    //duration时间计数单位, 一秒计数1000000次, 这个数值不一定有, 因为可能信息在流里,不在封装的文件头中
    this->totalMs = ic->duration/(AV_TIME_BASE)*1000;
    XLOGI("total ms = %d", totalMs);

    return true;
}

//读取一帧数据
XData FFDemux::Read() {
    if (!ic){
        return XData();
    }
    XData xData;
    AVPacket *pkt = av_packet_alloc();
    int re = av_read_frame(ic, pkt);
    if (re != 0){
        av_packet_free(&pkt);
        return XData();
    }
    XLOGI("pack size %d pts %lld", pkt->size, pkt->pts);
    xData.data = (unsigned char*)pkt;
    xData.size = pkt->size;


    return xData;
}

FFDemux::FFDemux() {
    //这样线程不安全, 创建对象注意不要同时创建
    static bool isFirst = true;
    if (isFirst){
        isFirst = false;
        //注册所有封装器
        av_register_all();
        //注册所有解码器
        avcodec_register_all();
        //初始化网络
        avformat_network_init();
        XLOGI("register ffmpeg");
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容