赞
踩
在windows上的视频流播放器有很多,而且各个监控厂家无论大厂还是小厂,基本上都提供了客户端,甚至很多第三方的监控平台软件厂商,也都提供了windows的版本,基本的都没有提供linux版本和mac系统版本,在当下国产操作系统(以linux内核为主)强势推进的大环境下,越来越多的需求是需要在linux对接视频监控设备,于是借助Qt这个超级强大跨平台的开发工具,直接移植到linux系统mac系统即可,顺带还有国产系统的视频监控系统平台。
移植最大的难点是如何兼容各大linux系统,经历过长达好几年的摸索探索,最终发现找一个低版本的linux系统安装低版本的Qt开发环境,静态编译可执行文件,ffmpeg动态库,这种组合适用硬件平台相同的所有linux系统,唯独位数不一样,32位和64位的要分别提供,而且不需要安装,绿色版运行,对可执行文件做了rpath设置,解压出来双击就能运行。
公众号:Qt实战,各种开源作品、经验整理、项目实战技巧,专注Qt/C++软件开发,视频监控、物联网、工业控制、嵌入式软件、国产化系统应用软件开发。
公众号:Qt入门和进阶,专门介绍Qt/C++相关知识点学习,帮助Qt开发者更好的深入学习Qt。多位Qt元婴期大神,一步步带你从入门到进阶,走上财务自由之路。
bool FFmpegThread::initVideo() { //找到视频流索引 videoIndex = av_find_best_stream(formatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); if (videoIndex < 0) { //有些没有视频流所以这里不用返回 debug("无视频流", ""); } else { //先获取旋转角度(如果有旋转角度则不能用硬件加速) this->getRotate(); if (rotate != 0) { hardware = "none"; } //获取视频流 int result = -1; AVStream *videoStream = formatCtx->streams[videoIndex]; //查找视频解码器(如果上面av_find_best_stream第五个参数传了则这里不需要) AVCodecID codecID = FFmpegHelper::getCodecID(videoStream); videoCodec = avcodec_find_decoder(codecID); //videoCodec = avcodec_find_decoder_by_name("h264"); if (!videoCodec) { debug("无解码器", "错误: 查找视频解码器失败"); return false; } //创建视频解码器上下文 videoCodecCtx = avcodec_alloc_context3(NULL); if (!videoCodecCtx) { debug("无解码器", "错误: 创建视频解码器上下文失败"); return false; } result = FFmpegHelper::copyContext(videoCodecCtx, videoStream, false); if (result < 0) { debug("无解码器", "错误: 设置视频解码器参数失败 " + FFmpegHelper::getError(result)); return false; } //初始化硬件加速(硬解码) if (!initHardware()) { return false; } //设置加速解码 设置lowres=max_lowres的话很可能画面采用最小的分辨率 //videoCodecCtx->lowres = videoCodec->max_lowres; videoCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; videoCodecCtx->flags |= AV_CODEC_FLAG_LOW_DELAY; videoCodecCtx->flags2 |= AV_CODEC_FLAG2_FAST; //打开视频解码器 result = avcodec_open2(videoCodecCtx, videoCodec, NULL); if (result < 0) { debug("打开解码", "错误: 打开视频解码器失败 " + FFmpegHelper::getError(result)); return false; } //获取分辨率大小 FFmpegHelper::getResolution(videoStream, videoWidth, videoHeight); //如果没有获取到宽高则返回 if (videoWidth == 0 || videoHeight == 0) { debug("无分辨率", ""); return false; } //记录首帧开始时间 videoFirstPts = videoStream->start_time; //帧率优先取平均帧率 frameRate = av_q2d(videoStream->avg_frame_rate); if (frameRate <= 0 || frameRate > 60) { frameRate = av_q2d(videoStream->r_frame_rate); } QString msg = QString("索引: %1 解码: %2 帧率: %3 宽高: %4x%5 角度: %6").arg(videoIndex).arg(videoCodec->name).arg(frameRate).arg(videoWidth).arg(videoHeight).arg(rotate); debug("视频信息", msg); } return true; } bool FFmpegThread::initHardware() { if (hardware == "none") { return true; } #if (FFMPEG_VERSION_MAJOR > 2) //根据名称自动寻找硬解码 enum AVHWDeviceType type; //发现嵌入式上低版本的库没有av_hwdevice_find_type_by_name函数 #ifdef __arm__ #if (FFMPEG_VERSION_MAJOR < 4) return false; #else type = av_hwdevice_find_type_by_name(hardware.toUtf8().data()); #endif #else type = av_hwdevice_find_type_by_name(hardware.toUtf8().data()); #endif debug("硬件加速", QString("名称: %1 数值: %2").arg(hardware).arg(type)); //找到对应的硬解码格式 FFmpegHelper::hw_pix_fmt = FFmpegHelper::find_fmt_by_hw_type(type); if (FFmpegHelper::hw_pix_fmt == -1) { debug("加速失败", "错误: 未找到对应加速类型"); return false; } int result = -1; //解码器格式赋值为硬解码 videoCodecCtx->get_format = FFmpegHelper::get_hw_format; //av_opt_set_int(videoCodecCtx, "refcounted_frames", 1, 0); //创建硬解码设备 AVBufferRef *hw_device_ref; result = av_hwdevice_ctx_create(&hw_device_ref, type, NULL, NULL, 0); if (result < 0) { debug("加速失败", "错误: 创建视频解码器失败 " + FFmpegHelper::getError(result)); return false; } videoCodecCtx->hw_device_ctx = av_buffer_ref(hw_device_ref); av_buffer_unref(&hw_device_ref); return true; #else return false; #endif } bool FFmpegThread::initAudio() { //找到音频流索引 audioIndex = av_find_best_stream(formatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0); if (audioIndex < 0) { //有些没有音频流所以这里不用返回 debug("无音频流", ""); } else { //获取音频流 int result = -1; AVStream *audioStream = formatCtx->streams[audioIndex]; //查找音频解码器(如果上面av_find_best_stream第五个参数传了则这里不需要) AVCodecID codecID = FFmpegHelper::getCodecID(audioStream); audioCodec = avcodec_find_decoder(codecID); //audioCodec = avcodec_find_decoder_by_name("aac"); if (!audioCodec) { debug("无解码器", "错误: 查找音频解码器失败"); return false; } //创建音频解码器上下文 audioCodecCtx = avcodec_alloc_context3(audioCodec); if (!audioCodecCtx) { debug("无解码器", "错误: 创建音频解码器上下文失败"); return false; } result = FFmpegHelper::copyContext(audioCodecCtx, audioStream, false); if (result < 0) { debug("无解码器", "错误: 设置音频解码器参数失败 " + FFmpegHelper::getError(result)); return false; } //打开音频解码器 result = avcodec_open2(audioCodecCtx, audioCodec, NULL); if (result < 0) { debug("打开解码", "错误: 打开音频解码器失败 " + FFmpegHelper::getError(result)); return false; } //记录首帧开始时间 audioFirstPts = audioStream->start_time; //获取音频参数 profile = audioCodecCtx->profile; sampleRate = audioCodecCtx->sample_rate; channelCount = 2;//audioCodecCtx->channels;//发现有些地址居然有6个声道 int sampleSize = 2;//av_get_bytes_per_sample(audioCodecCtx->sample_fmt) / 2; QString codecName = audioCodec->name;//long_name emit receiveAudioFormat(sampleRate, channelCount, sampleSize * 8); //如果解码不是aac且设置了保存为aac则强制改成保存为wav //有些文件就算音频解码是aac然后用aac保存输出可能也有错(只需要改成wav格式100%正确) if (codecName != "aac" && saveAudioType == SaveAudioType_Aac) { saveAudioType = SaveAudioType_Wav; } //音频采样转换 qint64 channelOut = AV_CH_LAYOUT_STEREO; qint64 channelIn = av_get_default_channel_layout(audioCodecCtx->channels); pcmSwrCtx = swr_alloc(); pcmSwrCtx = swr_alloc_set_opts(NULL, channelOut, AV_SAMPLE_FMT_S16, sampleRate, channelIn, audioCodecCtx->sample_fmt, sampleRate, 0, 0); //分配音频数据内存 192000 这个值是看ffplay代码中的 if (swr_init(pcmSwrCtx) >= 0) { quint64 byte = (192000 * 3) / 2; pcmData = (quint8 *)av_malloc(byte * sizeof(quint8)); if (!pcmData) { av_free(pcmData); return false; } } else { return false; } QString msg = QString("索引: %1 解码: %2 比特: %3 声道: %4 采样: %5 品质: %6").arg(audioIndex).arg(codecName).arg(formatCtx->bit_rate).arg(channelCount).arg(sampleRate).arg(profile); debug("音频信息", msg); } return true; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。