栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

FFmpeg拉流教程

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

FFmpeg拉流教程

FFmpeg拉流教程
做了一个项目学习下FFmpeg拉流的过程。在此记录下。
开发环境:
QT:MSVC 2017 64bit和Qt for android (跨平台)
FFmpeg:4.4.2
一、.pro的配置(此处贴出我的示例,请根据自己的环境配置):

win32{
	INCLUDEPATH += $$PWD/ffmpeg/include
	LIBS += $$PWD/ffmpeg/bin/avdevice.lib
	        $$PWD/ffmpeg/bin/avfilter.lib
	        $$PWD/ffmpeg/bin/avformat.lib
	        $$PWD/ffmpeg/bin/avutil.lib
	        $$PWD/ffmpeg/bin/postproc.lib
	        $$PWD/ffmpeg/bin/swresample.lib
	        $$PWD/ffmpeg/bin/swscale.lib
	        $$PWD/ffmpeg/bin/avcodec.lib
}
android{
INCLUDEPATH += $$PWD/AndroidFFmpeg/include
LIBS += $$PWD/AndroidFFmpeg/lib/libavformat.a
        $$PWD/AndroidFFmpeg/lib/libavcodec.a
        $$PWD/AndroidFFmpeg/lib/libavdevice.a
        $$PWD/AndroidFFmpeg/lib/libavfilter.a
        $$PWD/AndroidFFmpeg/lib/libavutil.a
        $$PWD/AndroidFFmpeg/lib/libswresample.a
        $$PWD/AndroidFFmpeg/lib/libswscale.a
}
note:据说FFmpeg库的加载顺序还有要求。
二、引入FFmpeg的头文件
extern "C" {
#include "libavutil/opt.h"
#include "libavutil/time.h"
#include "libavutil/frame.h"
#include "libavutil/pixdesc.h"
#include "libavutil/avassert.h"
#include "libavutil/imgutils.h"
#include "libavutil/ffversion.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libavformat/avformat.h"
#include "libavfilter/avfilter.h"
}

note:因为FFmpeg是用C语言的规则,所以此处引用头文件要加上extern “C”
三、FFmpeg的拉流

  1. FFmepg库的初始化(该语句必须且只能执行一次)
    avformat_network_init();

  2. 相关变量的定义
    int videoStreamIndex; //视频流索引
    AVPacket *avPacket; //包对象
    AVFrame *avFrame; //帧对象
    AVFormatContext *avFormatContext;//格式对象
    AVCodecContext *videoCodec; //视频解码器
    SwsContext *swsContext; //处理图片数据对象
    AVCodec *videoDecoder; //视频解码
    uint8_t *VideoData[AV_NUM_DATA_POINTERS];
    int lines[AV_NUM_DATA_POINTERS];
    QTime m_CurTime; //计算av_read_frame超时时间时使用
    QString m_strUrl;

  3. 打开URL
    .

  4. static int interruptCallback(void *context){
        QTime *pStartTime = (QTime *)(context);
        if (pStartTime == NULL) {
            return 0;
        }
        if( QTime::currentTime() > *pStartTime)
        {
            //超时
            qDebug()<<"Timeout;QTime::currentTime() =  "<interrupt_callback.callback = interruptCallback;
        avFormatContext->interrupt_callback.opaque =(void *)(&m_CurTime);
        avFormatContext->avio_flags |= AVIO_FLAG_NONBLOCK;
        const AVCodec *pCodec = NULL;
        m_CurTime = QTime::currentTime().addSecs(3);
        int result = avformat_open_input(&avFormatContext, m_strUrl.toStdString().c_str(), NULL, &options);
        if (result < 0) {
            return RET_ERROR_OPEN_INPUT;
        }
        //释放设置参数
        if (options != NULL) {
            av_dict_free(&options);
        }
         //查找流信息
         m_CurTime = QTime::currentTime().addSecs(3);
         if (avformat_find_stream_info(avFormatContext, NULL) < 0)
         {
            // emit ErrofInfo(QString("获取流信息错误"));
            return RET_ERROR_FIND_STREAM;
         }
         //查找输入流
         videoStreamIndex = -1;
         for (uint32_t i = 0; i < avFormatContext->nb_streams; i++)
         {
           AVStream *pStream = avFormatContext->streams[i];
           if (pStream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
           {
             videoCodec = avcodec_alloc_context3(NULL);
             avcodec_parameters_to_context(videoCodec, pStream->codecpar);
            pCodec = avcodec_find_decoder(videoCodec->codec_id);
             videoStreamIndex = i;
             break;
           }
         }
         if (!videoCodec)
         {
             return RET_ERROR_FIND_CODEC;
         }
         //打开视频流解码器失败
         int iRet = avcodec_open2(videoCodec, pCodec, NULL);
         if (iRet != 0)
         {
             emit ErrofInfo(QString("打开视频流解码器失败"));
             return RET_ERROR_OPEN_CODEC;
         }
         //打印关于输入或输出格式的详细信息
          av_dump_format(avFormatContext,videoStreamIndex, m_strUrl.toStdString().data(), 0);
          avPacket = av_packet_alloc();
          avFrame = av_frame_alloc();
          //获取缩放上下文失败
          swsContext = sws_getContext(videoCodec->width,videoCodec->height,videoCodec->pix_fmt
                                      ,videoCodec->width, videoCodec->height,AV_PIX_FMT_RGB24
                                      ,SWS_BICUBIC, NULL, NULL, NULL);
          if (swsContext == NULL)
         {
             return RET_ERROR_SWS_GET_CONTEXT;
         }
    
        char *rgb = new char[videoCodec->width*videoCodec->height*3];
        memset(rgb, 0 , videoCodec->width*videoCodec->height*3);
        VideoData[0] = (uint8_t *)rgb;
        lines[0] = videoCodec->width * 3;
        m_bInitFlag = true;
        return RET_SUCCESS;
    }
    

    通过线程拉流

    void FFmpegThread::run()
    {
        int index;
        int temp_ret = 0;
        char errors[1024] = "";
        while(!isInterruptionRequested()){
            m_CurTime = QTime::currentTime().addSecs(5);
            av_packet_unref(avPacket); //如果没有该语句av_read_frame会导致内存泄漏
           temp_ret = av_read_frame(avFormatContext, avPacket);
           if ( temp_ret < 0) {
               av_strerror(temp_ret,errors,sizeof(errors));
               qDebug()<<"av_read_frame return error" << (uint32_t)temp_ret << "info" << errors;
               emit ErrofInfo("视频连接断开,请检查网络连接!");
               break;
           }
           //判断当前包是视频还是音频
           index = avPacket->stream_index;
           if (index != videoStreamIndex) {
               //非视频流
                continue;
           }
           //解码视频流
           temp_ret = avcodec_send_packet(videoCodec,avPacket);
           if (0 != temp_ret) {
               //解码错误
               continue;
           }
            if(avcodec_receive_frame(videoCodec, avFrame) != temp_ret)
            {
                //从解码器中读取数据错误
               continue;
            }
           //将数据转成一张图片
            sws_scale(swsContext, (const uint8_t *const *)avFrame->data, avFrame->linesize, 0
                      , videoCodec->height, VideoData, lines);
           QImage image(VideoData[0], videoCodec->width,  videoCodec->height, QImage::Format_RGB888);
           if (!image.isNull()) {
               emit receiveImage(image);
           }
           av_packet_unref(avPacket);
        }
        //线程结束后释放资源
        free();
    }
    

    释放资源

    void FFmpegThread::free()
    {
        m_bInitFlag = false;
        if (swsContext != NULL) {
            sws_freeContext(swsContext);
            m_bInitFlag = NULL;
        }
    
        if (avPacket != NULL) {
            av_packet_unref(avPacket);
            avPacket = NULL;
        }
    
        if (avFrame != NULL) {
            av_frame_free(&avFrame);
            avFrame = NULL;
        }
        if (videoCodec != NULL) {
            avcodec_close(videoCodec);
            videoCodec = NULL;
        }
        if (avFormatContext != NULL) {
            avformat_close_input(&avFormatContext);
            avFormatContext = NULL;
        }
        if(VideoData[0]!= NULL)
        {
            delete []VideoData[0];
        }
    }
    

    音视频流程图(这两张图从公众号八小时码字员中摘取。链接: 八小时码字员)
    音视频解码流程:

    音视频播放流程:

    android版FFmpeg库资源:链接: Android版FFmpeg
    本人新手,有什么问题欢迎指教

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1034524.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号