天天看點

Qt+FFMPEG學習(一)視訊幀轉換為QImage

extern "C"{
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
    #include "libswscale/swscale.h"
    #include "libavdevice/avdevice.h"
    #include "libavformat/avio.h"
    #include "libavutil/imgutils.h"
}

#include <QString>
#include <QDebug>
#include <QImage>

int videoToImage(const QString &audioPath,const QString&outputDir){

    AVFormatContext* formatContext=avformat_alloc_context();                                    //配置設定fomat上下文

    if(avformat_open_input(&formatContext,audioPath.toLocal8Bit().data(),nullptr,nullptr)!=0){  //打開輸入流
        qDebug()<<"can`t open the file.";
        return -1;
    }
    
    av_dump_format(formatContext,0,audioPath.toLocal8Bit(),0);                                  //在終端列印
    
    if(avformat_find_stream_info(formatContext, nullptr)!=0){                                   //加載輸入流中的資訊
        qDebug()<<"can`t find stream infomation";
        return -1;
    }

    int videoStreamIndex=-1;
    for(uint i=0;i<formatContext->nb_streams;i++){
        if(formatContext->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO){                //查找第一個視訊流(一個視訊中可能有多個流(音頻+視訊))
            videoStreamIndex=i;
            break;
        }
    }

    if(videoStreamIndex==-1){
        qDebug()<<"didn`t find a video stream";
        return -1;
    }

    AVCodecParameters *codecParam=formatContext->streams[videoStreamIndex]->codecpar;           //擷取編解碼器的參數集
    AVCodec* codec=avcodec_find_decoder(codecParam->codec_id);                                  //擷取編解碼器
    AVCodecContext* codecContext=avcodec_alloc_context3(nullptr);                               //擷取編解碼上下文
    avcodec_parameters_to_context(codecContext,codecParam);                                     //根據編解碼器參數填充編解碼上下文

    if(codec==nullptr){
        qDebug()<<"can`t find codec.";
        return -1;
    }
    if( avcodec_open2(codecContext,codec,nullptr)!=0){                                          //開啟編解碼器
        qDebug()<<"can`t open codec";
        return -1;
    }
    
    AVPacket *packet = av_packet_alloc();                                                       //配置設定一個資料包

    AVFrame* frame=av_frame_alloc();                                                            //配置設定一個視訊幀
    
    QImage output(codecParam->width,codecParam->height,QImage::Format_RGB888);                  //構造一個QImage用作輸出
    int outputLineSize[3];                                                                         //構造AVFrame到QImage所需要的資料
    av_image_fill_linesizes(outputLineSize,AV_PIX_FMT_RGB24,codecParam->width);
    uint8_t *outputDst[]={output.bits()};
    
    //構造一個格式轉換上下文
    SwsContext *imgConvertContext=sws_getContext(codecParam->width,codecParam->height,(AVPixelFormat)codecParam->format,codecParam->width,codecParam->height,AV_PIX_FMT_BGR24,SWS_BICUBIC,NULL,NULL,NULL);
    
    int index = 0;
    while (true) {
        if(av_read_frame(formatContext,packet)<0)
            break;
        if(packet->stream_index==videoStreamIndex){
            if(avcodec_send_packet(codecContext,packet)!=0)
                continue;
            if(avcodec_receive_frame(codecContext,frame)!=0)
                continue;
            sws_scale(imgConvertContext,frame->data,frame->linesize,0,codecParam->height,outputDst,outputLineSize);
            output.save(outputDir + QString::number(index++)+".jpg");
        }
    }
    av_frame_free(&frame);
    av_packet_free(&packet);
    avcodec_free_context(&codecContext);
    avformat_close_input(&formatContext);
}

int main(int argc, char *argv[])
{
    videoToImage("./test.mp4","./images/");
    return 0;
}
           

繼續閱讀