c++解析二维码

使用ffmpeg对图片进行解码,zbar识别二维码。

我测试环境为: windows下32位, mingw与msvc编译器

ffmepg相关库下载地址: 

动态导入库和头文件:https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-20190813-8cd96e1-win32-shared.zip

动态运行库:https://ffmpeg.zeranoe.com/builds/win32/dev/ffmpeg-20190813-8cd96e1-win32-dev.zip

ZBar库(在安装时记得勾选Development Headers and Libraries选项,不然安安装目录下找不到头和库):http://sourceforge.net/projects/zbar/files/zbar/0.10/zbar-0.10-setup.exe/download

我以qt creator做个例子:

qmake配置

//添加对应库,下载的都是动态库来着,运行时记得让程序能找到对应动态库


INCLUDEPATH += C:/ffmpeg/include
LIBS += -L C:/ffmpeg/lib \
-lavcodec -lavformat -lavutil -lswscale

INCLUDEPATH += D:/ZBar/include
LIBS += D:/ZBar/lib/libzbar.dll.a

main.cpp 

#include "qrcode.h"

#include 
#include 
#include 

int main()
try
{
    const std::string &filename = "d:/qrcode.png";
    std::vector v;
    std::ifstream fin(filename.c_str(), std::ios::binary);
    if( !fin ) return 0;
    fin.seekg(0, std::ifstream::end);
    auto end = fin.tellg();
    fin.seekg(0, std::ifstream::beg);
    auto beg = fin.tellg();
    v.resize(end-beg);
    if( !fin.read( reinterpret_cast(&v[0]), v.size() ) )
        return 0;
    fin.close();

    QRCode code;//自己包装的类
    if( !code.load(&v[0], v.size()) )//内存流或者直接输入url
        std::cout << "failure : " << code.get_last_error() << std::endl;
    else
    {
       std::cout << std::endl;
       for( auto& x : code.get_result() )
       {
            std::cout << "result : " <

qrcode.h 

#ifndef QRCODE_H
#define QRCODE_H

#include 
#include 
#include 
#include 

class AVFormatContext;
class AVFrame;

class QRCode
{
    using FreeVector = std::vector>;//存放一些清理工作
public:
    bool load( const std::string& url );
    bool load( uint8_t const *buf, size_t bytes );//加载内存数据
    std::vector get_result()const{ return zbar_result;}
    std::string get_last_error() const{ return errorMsg;}

    QRCode();
    ~QRCode(){ if(errorBuf) delete []errorBuf;}
private:
    bool decode( std::shared_ptr freeVector, AVFormatContext* pFmtCtx );
    bool getCodeByZBar(std::shared_ptr freeVector, AVFrame* pic);
    std::string errorMsg;
    char *errorBuf;
    std::vector zbar_result;
};

#endif // QRCODE_H

qrcode.cpp 

#include "qrcode.h"

#include 
#include 
#include 
#include 
#include 
extern "C"
{
#include 
#include 
#include 
#include 
}

using MemStream = std::tuple; //tuple存储内存流信息


QRCode::QRCode():errorMsg(),errorBuf(nullptr)
{
    errorBuf = new char[AV_ERROR_MAX_STRING_SIZE];
    assert(errorBuf != nullptr);
}

//AVIOContext从内存流中读数据要用的回调
static int read_pkt( void *opaque, uint8_t *buf, int bytes )
{
    MemStream& data = *reinterpret_cast(opaque);
    size_t &st_len = std::get<1>(data);
    uint8_t const*&st_buf = std::get<0>(data);
    bytes = static_cast(bytes) <= st_len ? bytes : st_len;
    if( bytes == 0 ) return AVERROR_EOF;
    std::copy(st_buf, st_buf+bytes, buf);
    st_len -= bytes;
    return bytes;
}

bool QRCode::load( uint8_t const *buf, size_t bytes )
{
    zbar_result.clear();
    std::shared_ptr freeVector( new FreeVector, [](FreeVector* v){
        for( auto& x : *v)
        {
            x();
        }
        delete v;
    });
    MemStream *pData = new MemStream (buf, bytes);
    freeVector->emplace_back([=](){
        std::cout << "free pData" << std::endl;delete pData;
    });

    constexpr size_t avio_ctx_buffer_size = 1024*1024*8; //8M
    uint8_t *avio_ctx_buf = static_cast( av_malloc(avio_ctx_buffer_size) );
    if(avio_ctx_buf == nullptr )
    {
        errorMsg = std::string()+__FILE__+" "+std::to_string(__LINE__)+", avio_ctx_buf==nullptr";
        return false;
    }

    AVIOContext *p_avio_ctx = avio_alloc_context
            ( avio_ctx_buf, avio_ctx_buffer_size, 0, pData, &read_pkt, nullptr,nullptr);
    if( p_avio_ctx == nullptr )
    {
        av_free(avio_ctx_buf);
        errorMsg = std::string()+__FILE__+" "+std::to_string(__LINE__)+", p_avio_ctx==nullptr";
        return false;
    }
    freeVector->emplace_back([p_avio_ctx]()mutable{
        std::cout << "free avio_ctx_buf" << std::endl;
        if( p_avio_ctx )
            av_free(p_avio_ctx->buffer);
        std::cout << "free p_avio_ctx" << std::endl;
        avio_context_free(&p_avio_ctx);
    });

    AVFormatContext *pFmtCtx = avformat_alloc_context();
    if( pFmtCtx == nullptr )
    {
        errorMsg = std::string()+__FILE__+" "+std::to_string(__LINE__)+", pFmtCtx==nullptr";
        return false;
    }
    pFmtCtx->pb = p_avio_ctx;

    int ret = avformat_open_input(&pFmtCtx, nullptr, nullptr, nullptr );
    if( 0 > ret )
    {
        av_make_error_string(errorBuf, AV_ERROR_MAX_STRING_SIZE, ret);
        errorMsg =std::string()+ __FILE__+" "+std::to_string(__LINE__)+" "+errorBuf;
        return false;
    }
    freeVector->emplace_back([=]()mutable{
        std::cout << "free pFmtCtx" << std::endl;
        avformat_close_input(&pFmtCtx);
    });
    return decode(freeVector, pFmtCtx);
}

bool QRCode::load( const std::string& url )
{
    zbar_result.clear();
    std::shared_ptr freeVector( new FreeVector, [](FreeVector* v){
        for( auto& x : *v)
        {
            x();
        }
        delete v;
    });
    AVFormatContext *pFmtCtx = nullptr;
    int ret = avformat_open_input(&pFmtCtx, url.c_str(), nullptr, nullptr );
    if( 0 > ret )
    {
        av_make_error_string(errorBuf, AV_ERROR_MAX_STRING_SIZE, ret);
        errorMsg =std::string()+ __FILE__+" "+std::to_string(__LINE__)+" "+errorBuf;
        return false;
    }
    freeVector->emplace_back([=]()mutable{
        std::cout << "free pFmtCtx" << std::endl;
        avformat_close_input(&pFmtCtx);
    });
    return decode(freeVector, pFmtCtx);
}

bool QRCode::decode( std::shared_ptr freeVector, AVFormatContext* pFmtCtx )
{
    if( 0 > avformat_find_stream_info(pFmtCtx, nullptr) )
    {
        errorMsg =std::string()+ __FILE__+" "+std::to_string(__LINE__)+" avformat_find_stream_info";
        return false;
    }
    int video_stream_idx = av_find_best_stream(pFmtCtx, AVMEDIA_TYPE_VIDEO, -1,-1,nullptr, 0);
    if( video_stream_idx < 0 )
    {
        errorMsg =std::string()+ __FILE__+" "+std::to_string(__LINE__)+" can't not find steam";
        return false;
    }

    AVStream *st = pFmtCtx->streams[video_stream_idx];

    AVCodec *p_codec = avcodec_find_decoder( st->codecpar->codec_id );

    if( p_codec == nullptr )
    {
        errorMsg =std::string()+ __FILE__+" "+std::to_string(__LINE__)+" can't not find decoder";
        return false;
    }

    AVCodecContext *p_codec_ctx = avcodec_alloc_context3(p_codec);
    if( p_codec_ctx == nullptr )
    {
        errorMsg =std::string()+ __FILE__+" "+std::to_string(__LINE__)+" alloc codec ctx failure";
        return false;
    }
    freeVector->emplace_back([=]()mutable{
        std::cout << "free p_codec_ctx" << std::endl;
        avcodec_free_context(&p_codec_ctx);
    });
    if( 0 > avcodec_parameters_to_context(p_codec_ctx, st->codecpar) )
    {
        errorMsg =std::string()+ __FILE__+" "+std::to_string(__LINE__)+" avcodec_parameters_to_context";
        return false;
    }

    if( 0 > avcodec_open2(p_codec_ctx, p_codec, nullptr ) )
    {
        errorMsg =std::string()+ __FILE__+" "+std::to_string(__LINE__)+" open codec failure";
        return false;
    }

    bool isNeedSws = p_codec_ctx->pix_fmt != AV_PIX_FMT_GRAY8 ? true:false;

    SwsContext *sws_ctx = nullptr;

    AVFrame *dst_frame = nullptr;
    if( isNeedSws )
    {
        dst_frame = av_frame_alloc();
        if( dst_frame == nullptr )
        {
            errorMsg =std::string()+ __FILE__+" "+std::to_string(__LINE__)+"alloc dst_frame failure";
            return false;
        }
        freeVector->emplace_back([=]()mutable{
            std::cout << "free dst_frame" << std::endl;
           av_frame_free(&dst_frame);
        });
        dst_frame->width = p_codec_ctx->width;
        dst_frame->height = p_codec_ctx->height;
        dst_frame->format = AV_PIX_FMT_GRAY8;

        if( 0 > av_frame_get_buffer( dst_frame, 1 ) )
        {
            errorMsg =std::string()+ __FILE__+" "+std::to_string(__LINE__)+"av_frame_get_buffer failure";
            return false;
        }

        sws_ctx = sws_getContext(p_codec_ctx->width, p_codec_ctx->height, p_codec_ctx->pix_fmt,
                                             p_codec_ctx->width, p_codec_ctx->height, static_cast(dst_frame->format),
                                             SWS_BILINEAR, nullptr,nullptr, nullptr);
        if( sws_ctx == nullptr )
        {
            errorMsg =std::string()+ __FILE__+" "+std::to_string(__LINE__)+"sws_getContext failure";
            return false;
        }
        freeVector->emplace_back([=](){
            std::cout << "free sws_ctx" << std::endl;
            sws_freeContext(sws_ctx);
        });
    }

    AVPacket pkt;
    av_init_packet( &pkt );

    AVFrame *p_frame = av_frame_alloc();
    if( p_frame == nullptr )
    {
        errorMsg =std::string()+ __FILE__+" "+std::to_string(__LINE__)+"alloc p_frame failure";
        return false;
    }
    freeVector->emplace_back([=]()mutable{
        std::cout << "free p_frame" << std::endl;
       av_frame_free(&p_frame);
    });

    int ret;

    while( 0 <= (ret = av_read_frame(pFmtCtx, &pkt) ) )
    {
        std::shared_ptr guard( &pkt, [](AVPacket *p_pkt){
            av_packet_unref( p_pkt );
        });//保证while每次循环后unref pkt

        ret = avcodec_send_packet(p_codec_ctx, &pkt);
        if( ret < 0 )
        {
            errorMsg =std::string()+ __FILE__+" "+std::to_string(__LINE__)+" send_packet failure";
            return false;
        }

        while( ret >= 0 )
        {
            ret = avcodec_receive_frame(p_codec_ctx, p_frame);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            {
                break;
            }
            if( ret < 0 )
            {
                errorMsg =std::string()+ __FILE__+" "+std::to_string(__LINE__)+"receive_frame failure";
                return false;
            }

            AVFrame *pic = p_frame;
            if( isNeedSws )
            {
                sws_scale(sws_ctx, p_frame->data, p_frame->linesize, 0, p_codec_ctx->height, dst_frame->data, dst_frame->linesize);
                pic = dst_frame;
            }
            return this->getCodeByZBar( freeVector, pic );
        }
    }

    if( ret < 0 && ret != AVERROR_EOF )
    {
        errorMsg =std::string()+ __FILE__+" "+std::to_string(__LINE__)+"read_frame failure";
        return false;
    }

    return true;
}

bool QRCode::getCodeByZBar(std::shared_ptr, AVFrame* pic)
{
    int size = av_image_get_buffer_size( static_cast(pic->format), pic->width, pic->height, 1 );
    zbar::Image image(pic->width, pic->height, "Y800"
                            , pic->data[0], size);
    zbar::ImageScanner scan;
    scan.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_ENABLE, 1);
    scan.scan(image);
    for (zbar::SymbolIterator symbol = scan.get_results().symbol_begin();
                            symbol != image.symbol_end();++symbol)
     {
           zbar_result.emplace_back( symbol->get_data() );
     }
    if( zbar_result.empty() )
    {
        errorMsg = "zbar can't not parse, picture may be not a qrcode";
        return false;
    }
     // clean up
     image.set_data(NULL, 0);
     return true;
}

 

你可能感兴趣的:(c++解析二维码,解析二维码,读取二维码)