瀏覽代碼

视频图像时间段检查是否相似完成

DevYK 3 年之前
父節點
當前提交
495c17bfa1

+ 1 - 1
CMakeLists.txt

@@ -158,7 +158,7 @@ if (CMAKE_BUILD_TYPE AND (CMAKE_BUILD_TYPE STREQUAL "Debug"))
     message("Debug mode:${CMAKE_C_FLAGS_DEBUG}")
     add_executable(ImageBlurDetection_debug main.cpp
             ${Project_SRC}
-            count_down_latch.h
+            src/utils/count_down_latch.h
             ${JAVA_JNI_SRC}
             )
     target_link_libraries(ImageBlurDetection_debug -lpthread

+ 1 - 1
main.cpp

@@ -1,6 +1,6 @@
 #include <iostream>
 #include "src/detection/image_blur_detection.h"
-#include "count_down_latch.h"
+#include "src/utils/count_down_latch.h"
 
 static void *detection_thread(void *arg) {
     const char *filename = "/Users/devyk/Downloads/IMG_3067.PNG";

+ 45 - 10
src/ffmpeg/av_decode.cpp

@@ -81,9 +81,6 @@ static int av_decode(struct DecoderContext *pContext, AVPacket *pPacket, enum AV
             av_frame_free(&out_frame);
             break;
         }
-        if (pContext->frame_count > 0 && pContext->decode_frame_count >= pContext->frame_count)break;
-
-        pContext->decode_frame_count += 1;
 
         int64_t pts = -1;
         if (pPacket)
@@ -92,6 +89,24 @@ static int av_decode(struct DecoderContext *pContext, AVPacket *pPacket, enum AV
         else
             pts = out_frame->pts * (1000 *
                                     (av_q2d(pContext->avformatContext->streams[pContext->st_index[type]]->time_base)));
+        if (pContext->end_time > 0 && pts >= pContext->end_time)break;
+
+//        LOGE("decode is video=%d pts=%lld \n",pPacket->stream_index == pContext->st_index[AVMEDIA_TYPE_VIDEO],pts);
+        if (pts < pContext->start_time && pContext->start_time >= 0 && pContext->force_Iframe != 1) {
+            continue;
+        }
+
+        double def_frame_delay =
+                (double) (1000.0 / pContext->fps);
+        //如果持续时间很短,那么就用每帧固定延迟时间
+        if (out_frame->pkt_duration < 10) {
+            pContext->decode_frame_ms += (long) def_frame_delay;
+        } else {//使用更加精准的帧延迟时间
+            //根据每帧的延迟时间来相加,更加精准,有些 pkt_duration 持续时长是 0
+            pContext->decode_frame_ms += (int64_t) (out_frame->pkt_duration *
+                                                    ((AV_TIME_BASE *
+                                                      (av_q2d(pContext->avformatContext->streams[pContext->st_index[AVMEDIA_TYPE_VIDEO]]->time_base)))))/1000;
+        }
 
         out_frame->pts = pts;
         out_frame->pkt_dts = pts;
@@ -112,10 +127,10 @@ static int av_decode(struct DecoderContext *pContext, AVPacket *pPacket, enum AV
  * @param url 
  * @param outVideoFormat 
  * @param force_Iframe 
- * @param decode_frame_count 
+ * @param end_time
  * @return 
  */
-long initDecoder(const char *url, int force_Iframe, int decode_frame_count, DisableMediaType disableMediaType) {
+long initDecoder(const char *url, int force_Iframe, long start_time, int end_time, DisableMediaType disableMediaType) {
     struct DecoderContext *dctx = (struct DecoderContext *) malloc(sizeof(struct DecoderContext));
     if (!dctx) {
         LOGE("DecoderContext create fail.");
@@ -124,8 +139,9 @@ long initDecoder(const char *url, int force_Iframe, int decode_frame_count, Disa
     memset(dctx, 0, sizeof(struct DecoderContext));
     dctx->url = strdup(url);
     dctx->force_Iframe = force_Iframe;
-    dctx->frame_count = decode_frame_count;
-    dctx->ffthread_count = 3;
+    dctx->ffthread_count = 0;
+    dctx->start_time = start_time;
+    dctx->end_time = end_time;
     dctx->disableType = disableMediaType;
     char err_info[512] = {0};
     int ret = avformat_open_input(&(dctx->avformatContext), dctx->url, NULL, NULL);
@@ -148,11 +164,26 @@ long initDecoder(const char *url, int force_Iframe, int decode_frame_count, Disa
     dctx->st_index[AVMEDIA_TYPE_VIDEO] =
             av_find_best_stream(dctx->avformatContext, AVMEDIA_TYPE_VIDEO,
                                 dctx->st_index[AVMEDIA_TYPE_VIDEO], -1, NULL, 0);
+    if (dctx->st_index[AVMEDIA_TYPE_VIDEO] < 0) {
+        for (int i = 0; i < dctx->avformatContext->nb_streams; ++i) {
+            if (dctx->avformatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+                dctx->st_index[AVMEDIA_TYPE_VIDEO] = i;
+
+        }
+    }
     dctx->st_index[AVMEDIA_TYPE_AUDIO] = av_find_best_stream(dctx->avformatContext,
                                                              AVMEDIA_TYPE_AUDIO,
                                                              -1,
                                                              -1,
                                                              NULL, 0);
+
+    if (dctx->st_index[AVMEDIA_TYPE_AUDIO] < 0) {
+        for (int i = 0; i < dctx->avformatContext->nb_streams; ++i) {
+            if (dctx->avformatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
+                dctx->st_index[AVMEDIA_TYPE_AUDIO] = i;
+
+        }
+    }
     if (dctx->st_index[AVMEDIA_TYPE_VIDEO] >= 0) {
         AVStream *as = dctx->avformatContext->streams[dctx->st_index[AVMEDIA_TYPE_VIDEO]];
         dctx->v_codec_id = as->codecpar->codec_id;
@@ -188,8 +219,12 @@ long initDecoder(const char *url, int force_Iframe, int decode_frame_count, Disa
 
     }
     dctx->totalMs = dctx->avformatContext->duration / (AV_TIME_BASE / 1000);
-    LOGE("媒体总时长 totalMs=%lld \n", dctx->totalMs);
+    if (dctx->start_time > dctx->totalMs) {
+        LOGE("媒体总时长 totalMs=%lld start_time=%lld \n", dctx->totalMs, dctx->start_time);
+        return -1;
+    }
 
+    LOGE("媒体总时长 totalMs=%lld \n", dctx->totalMs);
     if (dctx->avformatContext->start_time != AV_NOPTS_VALUE)
         dctx->offset = dctx->avformatContext->start_time;
 
@@ -221,7 +256,7 @@ long initDecoder(const char *url, int force_Iframe, int decode_frame_count, Disa
         dctx->video_queue = new BlockQueue<AVFrame *>(dctx->fps);
     }
 
-    int64_t timestamp = 0;
+    int64_t timestamp = dctx->start_time * 1000;
     if (dctx->offset != AV_NOPTS_VALUE)
         timestamp += dctx->offset;
     if (timestamp > 0) {
@@ -307,7 +342,7 @@ void *av_read_decode_thread(void *pVoid) {
         struct DecoderContext *ctx = (struct DecoderContext *) pVoid;
         if (ctx && ctx->avformatContext) {
             while (1) {
-                if (ctx->decode_frame_count > ctx->frame_count && ctx->frame_count > 0)
+                if (ctx->decode_frame_ms > ctx->end_time && ctx->end_time > 0)
                     break;
                 AVPacket *pkg = av_packet_alloc();
                 ret = av_read_frame(ctx->avformatContext, pkg);

+ 4 - 4
src/ffmpeg/av_decode.h

@@ -66,9 +66,9 @@ typedef struct DecoderContext {
     enum AVCodecID v_codec_id, a_codec_id;
 
     /**
-     * 是否初始化成功
+     * 起点
      */
-    int isInitOK;
+    long start_time;
 
     /**
      * 关闭媒体资源类型
@@ -76,7 +76,7 @@ typedef struct DecoderContext {
     enum DisableMediaType disableType;
 
     /*强制 I frame 输出, 解码多少帧*/
-    int force_Iframe,decode_frame_count,frame_count;
+    long force_Iframe,decode_frame_ms,end_time;
     long totalMs;
 
     //音视频解码器缓存队列
@@ -87,7 +87,7 @@ typedef struct DecoderContext {
 };
 
 /*最简单的解码器实现*/
-long initDecoder(const char *url, int force_Iframe, int decode_frame_count,DisableMediaType disableMediaType);
+long initDecoder(const char *url, int force_Iframe,long start_time, int decode_frame_count, DisableMediaType disableMediaType);
 
 
 int av_read_decode_frame(long decodec_id);

+ 2 - 2
src/opencv/image_fingerprint.cpp

@@ -44,7 +44,7 @@ const char *fingerprintFromFFAVFrame(AVFrame *frame, int *len) {
         return NULL;
     }
     std::string str = frame->pts + "";
-    imshow(str, img);
+//    imshow(str, img);
     int scale_width = 8, scale_height = 8;
     Mat gray, res;
     //缩放成8x8大小灰度图
@@ -63,7 +63,7 @@ const char *fingerprintFromFFAVFrame(AVFrame *frame, int *len) {
     return buf;
 }
 
-float fingerprint_compare(char *arr, char *arr2, int len) {
+float fingerprint_compare(const char *arr,const char *arr2, int len) {
     int size = len;
     int sim_sum = 0;
     for (int i = 0; i < size; ++i) {

+ 8 - 1
src/opencv/image_fingerprint.h

@@ -26,7 +26,14 @@ AVFrame* cvmatToAvframe(cv::Mat* image, AVFrame * frame);
 
 const char *fingerprintFromFFAVFrame(AVFrame *frame, int *pInt);
 
-float fingerprint_compare(char * arr,char * arr2,int len);
+/**
+ * 指纹相识度
+ * @param arr
+ * @param arr2
+ * @param len
+ * @return
+ */
+float fingerprint_compare(const char * arr,const char * arr2,int len);
 
 char *fingerprintFromImagePath(const char *path);
 

+ 1 - 0
src/utils/CMakeLists.txt

@@ -4,5 +4,6 @@ set(UTILS_SRC ${UTILS_SRC}
         src/utils/video_similarity.cpp
         src/utils/video_similarity.h
         src/utils/pq_blockingqueue.hpp
+        src/utils/count_down_latch.h
         PARENT_SCOPE
         )

+ 0 - 0
count_down_latch.h → src/utils/count_down_latch.h


+ 5 - 0
src/utils/md5.c

@@ -198,3 +198,8 @@ int get_file_md5(const char *filename, char *dest) {
     close(fdf);
     return filelen;
 }
+
+int md5_is_exactly_the_same(const char *src1, const char *src2) {
+    if (!src1 || !src2)return -2;
+    return strcmp(src1, src2);
+}

+ 2 - 0
src/utils/md5.h

@@ -94,4 +94,6 @@ void MD5Decode(unsigned int *output, unsigned char *input, unsigned int len);
  */
 int get_file_md5(const char * filepath,char *dst);
 
+int md5_is_exactly_the_same(const char *src1,const char * src2);
+
 #endif //BYTESFLOW_OPENCV_MEDIA_MD5_H

+ 3 - 3
src/utils/video_similarity.cpp

@@ -6,7 +6,7 @@
 
 
 long video_similarity_detection_init(const char *url) {
-    VideoSimilarityContext *ctx = (VideoSimilarityContext *) malloc(sizeof(VideoSimilarityContext));
+    auto *ctx = (VideoSimilarityContext *) malloc(sizeof(VideoSimilarityContext));
     if (!ctx)return -1;
     memset(ctx, 0, sizeof(VideoSimilarityContext));
     ctx->video_path = strdup(url);
@@ -14,11 +14,11 @@ long video_similarity_detection_init(const char *url) {
 }
 
 
-int video_similarity_detection_start(long id, int force_keyframe, int frame_count, DisableMediaType disableMediaType,
+int video_similarity_detection_start(long id, int force_keyframe,long start_time, int frame_count, DisableMediaType disableMediaType,
                                      std::vector<VideoSimilarityModel *> &lists) {
     if (id <= 0)return -1;
     auto *ctx = (VideoSimilarityContext *) id;
-    long d_id = initDecoder(ctx->video_path, force_keyframe, frame_count, disableMediaType);
+    long d_id = initDecoder(ctx->video_path, force_keyframe, start_time, frame_count, disableMediaType);
     ctx->decode_obj_id = d_id;
     if (ctx->decode_obj_id <= 0)return ctx->decode_obj_id;
     int exit = 0, ret = 0;

+ 4 - 2
src/utils/video_similarity.h

@@ -22,7 +22,7 @@ typedef struct VideoSimilarityContext {
     long decode_obj_id;
 } VideoSimilarityContext;
 
-typedef struct VideoSimilarityModel{
+typedef struct VideoSimilarityModel {
     /*图片指纹个数*/
     int img_len;
     /*帧对应的指纹*/
@@ -42,9 +42,11 @@ long video_similarity_detection_init(const char *url);
 /**
  * 开始分析
  * @param force_keyframe 强制关键帧
+ * @param start_time 开始时间
  * @param frame_count 解码帧的数量
  * */
-int video_similarity_detection_start(long id,int force_keyframe, int frame_count,DisableMediaType disableMediaType,std::vector<VideoSimilarityModel*> &lists);
+int video_similarity_detection_start(long id, int force_keyframe, long start_time, int frame_count,
+                                     DisableMediaType disableMediaType, std::vector<VideoSimilarityModel *> &lists);
 
 /**
  * 关闭

+ 86 - 19
src/video_similarity_comparison.cpp

@@ -3,6 +3,34 @@
 //
 
 #include "utils/video_similarity.h"
+#include "utils/count_down_latch.h"
+
+extern "C" {
+#include "utils/md5.h"
+}
+
+typedef struct VsimBean {
+    const char *filepath;
+    const char *image_md5;
+    long start_time;
+    long end_time;
+    std::vector<VideoSimilarityModel *> lists;
+    pthread_t id;
+} VsimBean;
+
+static void *run_thread(void *p) {
+    auto *vsimBean = (VsimBean *) p;
+    long id = video_similarity_detection_init(vsimBean->filepath);
+    if (id > 0) {
+        int ret = video_similarity_detection_start(id, 0, vsimBean->start_time, vsimBean->end_time, AUDIO, vsimBean->lists);
+        char md5_str[64] = {0};
+//        if (get_file_md5(vsimBean->filepath, md5_str) > 0);
+//        vsimBean->image_md5 = strdup(md5_str);
+        video_similarity_detection_close(id);
+    }
+    return 0;
+}
+
 
 /**
  * 1、判断比对文件的 MD5 是否相等
@@ -15,28 +43,67 @@
  * @return
  */
 int main(int argc, char *argv[]) {
-    const char *filepath = "/Users/devyk/Data/Project/piaoquan/PQMedia/temp/4.mp4";
-    long id = video_similarity_detection_init(filepath);
-    std::vector<VideoSimilarityModel *> lists;
-    int ret = video_similarity_detection_start(id, 1, -1, AUDIO, lists);
-    for (int i = 0; i < lists.size(); ++i) {
-        auto model = lists[i];
-        int len = model->img_len;
-        const char *image_hash = model->image_hash;
-        for (int i = 0; i < len; ++i) {
-            printf("%d ", image_hash[i]);
+    int32_t size = 7;
+    const char *filepath[size];
+    //票圈视频iOS客户端上传
+    filepath[0] = "http://rescdn.yishihui.com/longvideo/video/vpc/20220304/20820121YCuumFJkPsqfRo51So";
+    //ios 原生上传
+    filepath[1] = "http://rescdn.yishihui.com/longvideo/video/vpc/20220304/20820121sxPL3akcURUIHquge3";
+    //iOS h5上传
+    filepath[2] = "http://rescdn.yishihui.com/longvideo/video/vpc/20220304/20820121rm4gHAdXW1IBr1ASEM";
+    //票圈视频安卓客户端上传
+    filepath[3] = "http://rescdn.yishihui.com/longvideo/video/vpc/20220304/200908721BH3GFTuCyThA6LMBY";
+    //安卓h5上传
+    filepath[4] = "http://rescdn.yishihui.com/longvideo/video/vpc/20220304/20090872465XAee2WXc6Pjz55z";
+    //安卓原生上传
+    filepath[5] = "http://rescdn.yishihui.com/longvideo/video/vpc/20220304/20090872DaWq15NTiSgmJvMNwC";
+//    filepath[5] = "http://rescdn.yishihui.com/longvideo/video/vpc/20220304/20090872TkYCoGR785GKZPPM7K";
+    http://rescdn.yishihui.com/longvideo/video/vpc/20220304/17938576TxEh0UJXpzcoYRJuHu
+    //本地 local 上传的视频
+    filepath[6] = "/Users/devyk/Downloads/share_18b774aab823c23d8072d7c45f8b9636.MP4";
+
+    VsimBean vsimBean[size];
+    for (int i = 0; i < size; ++i) {
+        vsimBean[i].filepath = strdup(filepath[i]);
+        vsimBean[i].start_time = 0;
+        vsimBean[i].end_time = -1;
+        pthread_create(&vsimBean[i].id, 0, run_thread, &vsimBean[i]);
+    }
+    for (int i = 0; i < size; ++i) {
+        pthread_join(vsimBean[i].id, 0);
+        LOGE("################# path=%s size=%d \n", vsimBean[i].filepath, vsimBean[i].lists.size());
+    }
+
+    //开始比较
+//    for (int i = 0; i < size; ++i) {
+    for (int i = 0; i < 1; ++i) {
+        auto a = vsimBean[6];
+        for (int j = i; j < size - 1; ++j) {
+            auto b = vsimBean[j];
+            //比较 md5
+//            if (md5_is_exactly_the_same(a.image_md5, b.image_md5) == 0) {
+//                LOGE("path1=%s path2=%s MD5 完全相似\n", a.filepath, b.filepath);
+//                continue;
+//            } else {
+//                LOGE("path1=%s path2=%s MD5 完全不相似\n", a.filepath, b.filepath);
+//            }
+            int minFrames = MIN(a.lists.size(), b.lists.size());
+            int maxFrames = MAX(a.lists.size(), b.lists.size());
+            int sim_frame_count = 0;
+            //比较帧指纹
+            for (int k = 0; k < minFrames; ++k) {
+                float v_sim = fingerprint_compare(a.lists[k]->image_hash, b.lists[k]->image_hash,
+                                                  MIN(a.lists[k]->img_len, b.lists[k]->img_len));
+                if (v_sim > 0.90) {
+                    sim_frame_count++;
+                }
+            }
+            LOGE("path1=%s path2=%s 相似度=%f   \n", a.filepath, b.filepath,
+                 (float) sim_frame_count / (float) maxFrames);
         }
-        printf("\n");
-        LOGE("frame-video I-Frame=%d pts=%lld\n", model->pict_type == AV_PICTURE_TYPE_I,
-             model->pts);
-        if (image_hash)
-            free((void *) image_hash);
-        delete model;
     }
-    lists.clear();
-    lists.shrink_to_fit();
 
-    video_similarity_detection_close(id);
+    LOGE("end \n");
     return 0;
 }