使用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;
}