Pārlūkot izejas kodu

VideoEncode中的ffmpeg升级为4.2.3,新增ROI encoding测试

huihui 5 gadi atpakaļ
vecāks
revīzija
192b413265

+ 8 - 0
source/FFMPEG_Qt/src/ffmpeg.c

@@ -704,6 +704,8 @@ static void close_all_output_streams(OutputStream *ost, OSTFinished this_stream,
 
 static void write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int unqueue)
 {
+fprintf(stderr, "[=] size=%d pts=%I64d %I64d index=%d \n", pkt->size, pkt->pts, pkt->dts, pkt->stream_index);
+
     AVFormatContext *s = of->ctx;
     AVStream *st = ost->st;
     int ret;
@@ -827,6 +829,8 @@ static void write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int u
                 pkt->size
               );
     }
+static int frameNums=0;
+fprintf(stderr, "[- %d] size=%d pts=%I64d %I64d index=%d \n", frameNums++, pkt->size, pkt->pts, pkt->dts, pkt->stream_index);
 
     ret = av_interleaved_write_frame(s, pkt);
     if (ret < 0) {
@@ -4441,6 +4445,9 @@ static int process_input(int file_index)
         }
     }
 
+//    static int frameNums=0;
+//fprintf(stderr, "[%d] size=%d pts=%I64d %I64d index=%d  \n", frameNums++, pkt.size, pkt.pts, pkt.dts, pkt.stream_index);
+
     if (pkt.dts != AV_NOPTS_VALUE)
         pkt.dts += av_rescale_q(ifile->ts_offset, AV_TIME_BASE_Q, ist->st->time_base);
     if (pkt.pts != AV_NOPTS_VALUE)
@@ -4529,6 +4536,7 @@ static int process_input(int file_index)
                av_ts2str(input_files[ist->file_index]->ts_offset),
                av_ts2timestr(input_files[ist->file_index]->ts_offset, &AV_TIME_BASE_Q));
     }
+//fprintf(stderr, "[%d] size=%d pts=%I64d %I64d index=%d \n", frameNums, pkt.size, pkt.pts, pkt.dts, pkt.stream_index);
 
     sub2video_heartbeat(ist, pkt.pts);
 

BIN
source/FFMPEG_Qt/常用命令/将音频左右声道分离.docx


+ 4 - 41
source/VideoEncode/VideoEncode.pro

@@ -19,6 +19,10 @@ contains(QT_ARCH, i386) {
     DESTDIR = $${PWD}/bin64
 }
 
+include($$PWD/lib/lib.pri)
+
+INCLUDEPATH += $$PWD/src
+
 SOURCES += \
         src/AppConfig.cpp \
         src/Mutex/Cond.cpp \
@@ -34,44 +38,3 @@ HEADERS += \
     src/Video/GetVideoThread.h \
     src/Video/VideoEncoder.h
 
-win32{
-
-    contains(QT_ARCH, i386) {
-        message("32-bit")
-        INCLUDEPATH += $$PWD/lib/win32/ffmpeg/include \
-                       $$PWD/src
-
-        LIBS += -L$$PWD/lib/win32/ffmpeg/lib -lavcodec -lavdevice -lavfilter -lavformat -lavutil -lpostproc -lswresample -lswscale
-
-    } else {
-        message("64-bit")
-        INCLUDEPATH += $$PWD/lib/win64/ffmpeg/include \
-                       $$PWD/src
-
-        LIBS += -L$$PWD/lib/win64/ffmpeg/lib -lavcodec -lavdevice -lavfilter -lavformat -lavutil -lpostproc -lswresample -lswscale
-
-    }
-
-}
-
-unix{
-    contains(QT_ARCH, i386) {
-        message("32-bit, 请自行编译32位库!")
-    } else {
-        message("64-bit")
-        INCLUDEPATH += $$PWD/lib/linux/ffmpeg/include \
-                       $$PWD/src
-
-        LIBS += -L$$PWD/lib/linux/ffmpeg/lib  -lavformat  -lavcodec -lavdevice -lavfilter -lavutil -lswresample -lswscale -lpostproc
-
-        LIBS += -lpthread -ldl
-    }
-
-#QMAKE_POST_LINK 表示编译后执行内容
-#QMAKE_PRE_LINK 表示编译前执行内容
-
-#解压库文件
-#QMAKE_PRE_LINK += "cd $$PWD/lib/linux && tar xvzf ffmpeg.tar.gz "
-system("cd $$PWD/lib/linux && tar xvzf ffmpeg.tar.gz")
-
-}

+ 2 - 1
source/VideoEncode/src/AppConfig.h

@@ -1,9 +1,10 @@
-#ifndef APPCONFIG_H
+#ifndef APPCONFIG_H
 #define APPCONFIG_H
 
 #include <stdio.h>
 #include <string.h>
 #include <string>
+#include <stdint.h>
 
 #if defined(WIN32)
 

+ 200 - 11
source/VideoEncode/src/Video/VideoEncoder.cpp

@@ -7,6 +7,9 @@
 #include "AppConfig.h"
 #include "VideoEncoder.h"
 
+//是否启用 ROI (region of interest) encoding 感兴趣区域视频编码技术
+#define TEST_ROI_ENCODING
+
 //#define ENCODE_H265
 
 VideoEncoder::VideoEncoder()
@@ -106,6 +109,38 @@ void VideoEncoder::run()
 
             memcpy(picture_buf, node.buffer, node.size);
 
+#ifdef TEST_ROI_ENCODING
+            {
+                // 申请side_data空间,假设有2个区域。
+                // 不需要显式释放申请到的空间,
+                // 在AVFrame的清理函数中会自动处理。
+                AVFrameSideData *sd = av_frame_new_side_data(picture,
+                             AV_FRAME_DATA_REGIONS_OF_INTEREST,
+                             1*sizeof(AVRegionOfInterest));
+
+                // 获取刚申请到的内存地址
+                AVRegionOfInterest* roi = (AVRegionOfInterest*)sd->data;
+
+
+                // 设置第一个区域
+                roi[0].self_size = sizeof(*roi);
+                roi[0].top       = 0;
+                roi[0].bottom    = 600;
+                roi[0].left      = 600;
+                roi[0].right     = 1000;
+                roi[0].qoffset   = AVRational{-1,1};
+
+//                // 设置第二个区域
+//                roi[1].self_size = sizeof(*roi);
+//                roi[1].top       = ...;
+//                roi[1].bottom    = ...;
+//                roi[1].left      = ...;
+//                roi[1].right     = ...;
+//                roi[1].qoffset   = ...;
+
+            }
+#endif
+
             int ret = avcodec_send_frame(pCodecCtx, picture);
             if (ret != 0)
             {
@@ -123,7 +158,9 @@ void VideoEncoder::run()
 
                 fprintf(stderr, "%s encoded size=%d isKeyFrame=%d\n", __FUNCTION__, pkt.size, isKeyFrame);
             }
-
+#ifdef TEST_ROI_ENCODING
+            av_frame_remove_side_data(picture, AV_FRAME_DATA_REGIONS_OF_INTEREST);
+#endif
             av_packet_unref(&pkt);
             av_free(node.buffer);
         }
@@ -155,6 +192,156 @@ void VideoEncoder::run()
 
 }
 
+#ifdef TEST_ROI_ENCODING
+AVDictionary *VideoEncoder::setEncoderParam(const AVCodecID &codec_id)
+{
+    int in_w = mWidth;
+    int in_h = mHeight;//宽高
+
+    int mFrameRate = 15;
+    int mQuality = 0;
+
+    pCodecCtx->codec_id = codec_id;
+    pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
+    pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
+    pCodecCtx->width = in_w;
+    pCodecCtx->height = in_h;
+    pCodecCtx->time_base.num = 1;
+    pCodecCtx->time_base.den = mFrameRate;//帧率(既一秒钟多少张图片)
+    pCodecCtx->bit_rate = 100000; //比特率(调节这个大小可以改变编码后视频的质量)
+    pCodecCtx->gop_size = 12;
+    //H264 还可以设置很多参数 自行研究吧
+    ////    pCodecCtx->me_range = 16;
+    ////    pCodecCtx->max_qdiff = 4;
+    ////    pCodecCtx->qcompress = 0.6;
+    ////    pCodecCtx->qmin = 10;
+    ////    pCodecCtx->qmax = 51;
+    //    pCodecCtx->me_range = 16;
+    //    pCodecCtx->max_qdiff = 1;
+    //    pCodecCtx->qcompress = 0.6;
+    //    pCodecCtx->qmin = 10;
+    //    pCodecCtx->qmax = 51;
+    //    //Optional Param
+    //    pCodecCtx->max_b_frames=3;
+
+    //    视频编码器常用的码率控制方式包括abr(平均码率),crf(恒定码率),cqp(恒定质量),
+    //    ffmpeg中AVCodecContext显示提供了码率大小的控制参数,但是并没有提供其他的控制方式。
+    //    ffmpeg中码率控制方式分为以下几种情况:
+    //    1.如果设置了AVCodecContext中bit_rate的大小,则采用abr的控制方式;
+    //    2.如果没有设置AVCodecContext中的bit_rate,则默认按照crf方式编码,crf默认大小为23(此值类似于qp值,同样表示视频质量);
+    //    3.如果用户想自己设置,则需要借助av_opt_set函数设置AVCodecContext的priv_data参数。下面给出三种控制方式的实现代码:
+
+#if 0
+    ///平均码率
+    //目标的码率,即采样的码率;显然,采样码率越大,视频大小越大
+    pCodecCtx->bit_rate = mBitRate;
+
+#elif 1
+    ///恒定码率
+//    量化比例的范围为0~51,其中0为无损模式,23为缺省值,51可能是最差的。该数字越小,图像质量越好。从主观上讲,18~28是一个合理的范围。18往往被认为从视觉上看是无损的,它的输出视频质量和输入视频一模一样或者说相差无几。但从技术的角度来讲,它依然是有损压缩。
+//    若Crf值加6,输出码率大概减少一半;若Crf值减6,输出码率翻倍。通常是在保证可接受视频质量的前提下选择一个最大的Crf值,如果输出视频质量很好,那就尝试一个更大的值,如果看起来很糟,那就尝试一个小一点值。
+//    av_opt_set(pCodecCtx->priv_data, "crf", "51.000", AV_OPT_SEARCH_CHILDREN);
+
+#else
+    ///qp的值和crf一样
+//    av_opt_set(pCodecCtx->priv_data, "qp", "31.000",AV_OPT_SEARCH_CHILDREN);
+#endif
+
+#if TEST_OPT
+
+    av_opt_set(pCodecCtx,"b","400000",0);		//bitrate
+    //Another method
+    //av_opt_set_int(pCodecCtx,"b",400000,0);	//bitrate
+
+    av_opt_set(pCodecCtx,"time_base","1/25",0);	//time_base
+    av_opt_set(pCodecCtx,"bf","5",0);			//max b frame
+    av_opt_set(pCodecCtx,"g","25",0);			//gop
+    av_opt_set(pCodecCtx,"qmin","10",0);		//qmin/qmax
+    av_opt_set(pCodecCtx,"qmax","51",0);
+
+#else
+
+//    pCodecCtx->time_base.num = 1;
+//    pCodecCtx->time_base.den = 25;
+//    pCodecCtx->max_b_frames=5;
+//    pCodecCtx->bit_rate = 400000;
+//    pCodecCtx->gop_size=25;
+//    pCodecCtx->qmin = 10;
+//    pCodecCtx->qmax = 51;
+
+    /* time base: this is the fundamental unit of time (in seconds) in terms
+       of which frame timestamps are represented. for fixed-fps content,
+       timebase should be 1/framerate and timestamp increments should be
+       identically 1. */
+//    ost->st->time_base = { 1, m_videoFrameRate };
+//    pCodecCtx->time_base       = ost->st->time_base;
+//    c->gop_size = 12; /* emit one intra frame every twelve frames at most */
+//    pCodecCtx->gop_size = m_videoFrameRate * 2; ///I帧间隔
+
+//    //固定允许的码率误差,数值越大,视频越小
+//    c->bit_rate_tolerance = mBitRate;
+
+//    //H264 还可以设置很多参数 自行研究吧
+////    pCodecCtx->me_range = 16;
+////    pCodecCtx->max_qdiff = 1;
+//    c->qcompress = 0.85;
+//    c->qmin = 18;
+
+    pCodecCtx->qmin = 0;
+    pCodecCtx->qmax = 51;
+
+//    pCodecCtx->qmin = 16+(10-mQuality)*2;
+//    pCodecCtx->qmax = 31+(10-mQuality)*2;
+
+////    //采用(qmin/qmax的比值来控制码率,1表示局部采用此方法,0表示全局)
+////    c->rc_qsquish = 0;
+
+////    //因为我们的量化系数q是在qmin和qmax之间浮动的,
+////    //qblur表示这种浮动变化的变化程度,取值范围0.0~1.0,取0表示不削减
+////    c->qblur = 1.0;
+
+//std::cout<<"mBitRate"<<mBitRate<<m_videoFrameRate<<std::endl;
+
+////    ///b_frame_strategy
+////    ///如果为true,则自动决定什么时候需要插入B帧,最高达到设置的最大B帧数。
+////    ///如果设置为false,那么最大的B帧数被使用。
+////    c->b_frame_strategy = 1;
+////    c->max_b_frames = 5;
+
+#endif
+
+    // some formats want stream headers to be separate
+    if (pCodecCtx->flags & AVFMT_GLOBALHEADER)
+        pCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
+
+    //编码器预设
+    AVDictionary *param = 0;
+    if(pCodecCtx->codec_id == AV_CODEC_ID_H264)
+    {
+//        //H.264
+//        //av_dict_set(&param, "preset", "slow", 0);
+//        av_dict_set(&param, "preset", "superfast", 0);
+//        av_dict_set(&param, "tune", "zerolatency", 0);  //实现实时编码
+
+        ///下面的可以启用硬件编码
+        av_dict_set(&param, "preset", "medium", 0);
+    //        av_dict_set(&param, "preset", "superfast", 0);
+        av_dict_set(&param, "tune", "zerolatency", 0); //实现实时编码
+        av_dict_set(&param, "profile", "main", 0);
+    }
+    else if(pCodecCtx->codec_id == AV_CODEC_ID_H265)
+    {
+        av_dict_set(&param, "preset", "ultrafast", 0);
+        av_dict_set(&param, "tune", "zerolatency", 0);
+        av_dict_set(&param, "profile", "main", 0);
+    }
+
+    return param;
+
+}
+
+#else
+
 AVDictionary *VideoEncoder::setEncoderParam(const AVCodecID &codec_id)
 {
     int in_w = mWidth;
@@ -208,6 +395,8 @@ AVDictionary *VideoEncoder::setEncoderParam(const AVCodecID &codec_id)
 
 }
 
+#endif
+
 ///打开视频编码器
 bool VideoEncoder::openVideoEncoder(const AVCodecID &codec_id)
 {
@@ -216,16 +405,16 @@ bool VideoEncoder::openVideoEncoder(const AVCodecID &codec_id)
 
 //    bool mIsSupportHardEncoder = true;
 //    if (mIsSupportHardEncoder)
-    {
-        ///尝试打开cuvid编码器器
-        isHardWareEncoderOpened = openHardEncoder_Cuvid(codec_id);
-
-        ///cuvid打开失败了 继续尝试 qsv
-        if (!isHardWareEncoderOpened)
-        {
-            isHardWareEncoderOpened = openHardEncoder_Qsv(codec_id);
-        }
-    }
+//    {
+//        ///尝试打开cuvid编码器器
+//        isHardWareEncoderOpened = openHardEncoder_Cuvid(codec_id);
+
+//        ///cuvid打开失败了 继续尝试 qsv
+//        if (!isHardWareEncoderOpened)
+//        {
+//            isHardWareEncoderOpened = openHardEncoder_Qsv(codec_id);
+//        }
+//    }
 
     //尝试打开硬件解码器失败了 改用软解码
     if (!isHardWareEncoderOpened)

+ 2 - 0
source/VideoEncode/src/Video/VideoEncoder.h

@@ -17,6 +17,8 @@ extern "C"
     #include "libavformat/avformat.h"
     #include "libswscale/swscale.h"
     #include "libavdevice/avdevice.h"
+
+    #include <libavutil/frame.h>
 }
 
 struct FrameDataNode

+ 1 - 1
source/VideoEncode/src/main.cpp

@@ -1,4 +1,4 @@
-#include <iostream>
+#include <iostream>
 
 using namespace std;