Jelajahi Sumber

添加水印

zhuizhu 8 bulan lalu
induk
melakukan
fea60d68fc

+ 26 - 19
AvPlayer2/audio_decode_thread.cpp

@@ -7,9 +7,14 @@
 // ***********************************************************/
 
 #include "audio_decode_thread.h"
-#include "AVPlayer2/playercontroller.h"
 #include <QLoggingCategory>
+#include "AVPlayer2/playercontroller.h"
+
+#define debug 0
+
+#if debug
 Q_LOGGING_CATEGORY(playerControllerAudioDecodeThread, "player.controller.AudioDecodeThread")
+#endif
 
 AudioDecodeThread::AudioDecodeThread(VideoState* pState)
     : m_pState(pState)
@@ -17,17 +22,19 @@ AudioDecodeThread::AudioDecodeThread(VideoState* pState)
 
 AudioDecodeThread::~AudioDecodeThread()
 {
+#if debug
     qCDebug(playerControllerAudioDecodeThread)
         << "[AudioDecodeThread] ~AudioDecodeThread, m_pState:" << (void*) m_pState;
+#endif
 }
 
 void AudioDecodeThread::run()
 {
-    qCDebug(playerControllerAudioDecodeThread) << "[AudioDecodeThread] run start, m_pState:" << (void*) m_pState;
+    //qCDebug(playerControllerAudioDecodeThread) << "[AudioDecodeThread] run start, m_pState:" << (void*) m_pState;
     assert(m_pState);
     VideoState* is = m_pState;
-    qCDebug(playerControllerAudioDecodeThread) << "[AudioDecodeThread] VideoState* is:" << (void*) is
-                                 << ", abort_request:" << is->abort_request;
+    // qCDebug(playerControllerAudioDecodeThread) << "[AudioDecodeThread] VideoState* is:" << (void*) is
+    //                              << ", abort_request:" << is->abort_request;
     AVFrame* frame = av_frame_alloc();
     Frame* af;
 #if USE_AVFILTER_AUDIO
@@ -40,31 +47,31 @@ void AudioDecodeThread::run()
     int ret = 0;
 
     if (!frame) {
-        qCWarning(playerControllerAudioDecodeThread) << "[AudioDecodeThread] av_frame_alloc failed!";
+        //qCWarning(playerControllerAudioDecodeThread) << "[AudioDecodeThread] av_frame_alloc failed!";
         return;
     }
 
     do {
         if (is->abort_request) {
-            qCDebug(playerControllerAudioDecodeThread) << "[AudioDecodeThread] abort_request set, exit.";
+            //qCDebug(playerControllerAudioDecodeThread) << "[AudioDecodeThread] abort_request set, exit.";
             break;
         }
         if (isExit()) {
-            qCDebug(playerControllerAudioDecodeThread) << "[AudioDecodeThread] m_exit set, exit.";
+            // qCDebug(playerControllerAudioDecodeThread) << "[AudioDecodeThread] m_exit set, exit.";
             break;
         }
-        qCDebug(playerControllerAudioDecodeThread)
-            << "[AudioDecodeThread] call decoder_decode_frame, auddec.avctx:"
-            << (void*) is->auddec.avctx;
+        // qCDebug(playerControllerAudioDecodeThread)
+        //    << "[AudioDecodeThread] call decoder_decode_frame, auddec.avctx:"
+        //    << (void*) is->auddec.avctx;
         if ((got_frame = decoder_decode_frame(&is->auddec, frame, nullptr)) < 0) {
-            qCWarning(playerControllerAudioDecodeThread)
-                << "[AudioDecodeThread] decoder_decode_frame failed, ret:" << got_frame;
+            //  qCWarning(playerControllerAudioDecodeThread)
+            //       << "[AudioDecodeThread] decoder_decode_frame failed, ret:" << got_frame;
             goto the_end;
         }
         if (got_frame) {
-            qCDebug(playerControllerAudioDecodeThread)
-                << "[AudioDecodeThread] got audio frame, pts:" << frame->pts
-                << ", sample_rate:" << frame->sample_rate << ", nb_samples:" << frame->nb_samples;
+            //   qCDebug(playerControllerAudioDecodeThread)
+            //       << "[AudioDecodeThread] got audio frame, pts:" << frame->pts
+            //       << ", sample_rate:" << frame->sample_rate << ", nb_samples:" << frame->nb_samples;
             tb = AVRational{1, frame->sample_rate};
 
 #if USE_AVFILTER_AUDIO
@@ -139,7 +146,7 @@ void AudioDecodeThread::run()
                 is->auddec.finished = is->auddec.pkt_serial;
 #endif
         } else {
-            qCDebug(playerControllerAudioDecodeThread) << "[AudioDecodeThread] no frame decoded, continue.";
+            //qCDebug(playerControllerAudioDecodeThread) << "[AudioDecodeThread] no frame decoded, continue.";
         }
     } while (got_frame);
 
@@ -155,8 +162,8 @@ the_end:
 
     av_frame_free(&frame);
     // 可加日志输出
-    qCDebug(playerControllerAudioDecodeThread) << "[AudioDecodeThread] run end, abort_request:"
-                                 << is->abort_request
-                                 << ", m_exit:" << (m_exit ? m_exit->load() : -1);
+    // qCDebug(playerControllerAudioDecodeThread) << "[AudioDecodeThread] run end, abort_request:"
+    //                              << is->abort_request
+    //                              << ", m_exit:" << (m_exit ? m_exit->load() : -1);
     return;
 }

+ 1 - 1
AvPlayer2/mainwindowa.cpp

@@ -1060,7 +1060,7 @@ void MainWindowA::set_play_speed()
 
     auto speed = pPlayControl->get_speed();
 
-    // qDebug("set_play_spped, speed control changed, speed:%d", speed);
+    qDebug("set_play_spped, speed control changed, speed:%d", speed);
     if (m_playerController) {
         if (auto pState = m_playerController->state()) {
 #if USE_AVFILTER_AUDIO

+ 73 - 4
AvPlayer2/packets_sync.cpp

@@ -684,7 +684,7 @@ void stream_seek(VideoState* is, int64_t pos, int64_t rel, int by_bytes)
         is->seek_flags &= ~AVSEEK_FLAG_BYTE;
         if (by_bytes)
             is->seek_flags |= AVSEEK_FLAG_BYTE;
-        is->seek_flags |= AVSEEK_FLAG_BACKWARD;
+        is->seek_flags |= AVSEEK_FLAG_ANY;
         is->seek_req = 1;
         // SDL_CondSignal(is->continue_read_thread);
         is->continue_read_thread->wakeAll();
@@ -883,15 +883,79 @@ void set_audio_playspeed(VideoState* is, double value)
     is->req_afilter_reconfigure = 1;
 }
 
+void set_video_watermark(VideoState* is, const char* watermark_text)
+{
+    if (!is || !is->video_st) {
+        return;
+    }
+
+    // 获取当前播放时间
+    double current_time = get_master_clock(is);
+    if (isnan(current_time)) current_time = 0.0;
+    
+    // 获取总时长
+    double total_duration = 0.0;
+    if (is->ic && is->ic->duration != AV_NOPTS_VALUE) {
+        total_duration = is->ic->duration / (double)AV_TIME_BASE;
+    }
+    
+    // 格式化时间显示
+    int cur_hours = (int)(current_time / 3600);
+    int cur_mins = (int)((current_time - cur_hours * 3600) / 60);
+    int cur_secs = (int)(current_time - cur_hours * 3600 - cur_mins * 60);
+    
+    int total_hours = (int)(total_duration / 3600);
+    int total_mins = (int)((total_duration - total_hours * 3600) / 60);
+    int total_secs_int = (int)(total_duration - total_hours * 3600 - total_mins * 60);
+    
+    size_t len = 512;
+    if (!is->vfilters)
+        is->vfilters = (char*) av_malloc(len);
+
+    snprintf(is->vfilters,
+             len,
+             "drawtext=fontfile='C\\:/Windows/Fonts/"
+             "arial.ttf':text='%02d\\:%02d\\:%02d / %02d\\:%02d\\:%02d':x=10:y=10:fontsize=24:fontcolor=white",
+             cur_hours, cur_mins, cur_secs, total_hours, total_mins, total_secs_int);
+
+    is->req_vfilter_reconfigure = 1;
+
+    qDebug("setting video watermark to :%s", is->vfilters);
+}
+
 void set_video_playspeed(VideoState* is)
 {
     double speed = is->audio_speed;
 
-    size_t len = 32;
+    // 获取当前播放时间
+    double current_time = get_master_clock(is);
+    if (isnan(current_time)) current_time = 0.0;
+    
+    // 获取总时长
+    double total_duration = 0.0;
+    if (is->ic && is->ic->duration != AV_NOPTS_VALUE) {
+        total_duration = is->ic->duration / (double)AV_TIME_BASE;
+    }
+    
+    // 格式化时间显示
+    int cur_hours = (int)(current_time / 3600);
+    int cur_mins = (int)((current_time - cur_hours * 3600) / 60);
+    int cur_secs = (int)(current_time - cur_hours * 3600 - cur_mins * 60);
+    
+    int total_hours = (int)(total_duration / 3600);
+    int total_mins = (int)((total_duration - total_hours * 3600) / 60);
+    int total_secs_int = (int)(total_duration - total_hours * 3600 - total_mins * 60);
+
+    size_t len = 512; // 增加缓冲区大小以容纳水印滤镜
     if (!is->vfilters)
         is->vfilters = (char*) av_malloc(len);
 
-    snprintf(is->vfilters, len, "setpts=%.4lf*PTS", 1.0 / speed);
+    // 组合播放速度和水印滤镜
+    snprintf(is->vfilters,
+             len,
+             "setpts=%.4lf*PTS,drawtext=fontfile='C\\:/Windows/Fonts/"
+             "arial.ttf':text='%02d\\:%02d\\:%02d / %02d\\:%02d\\:%02d':x=10:y=10:fontsize=24:fontcolor=white",
+             1.0 / speed, cur_hours, cur_mins, cur_secs, total_hours, total_mins, total_secs_int);
 
     is->req_vfilter_reconfigure = 1;
 
@@ -926,6 +990,9 @@ int configure_filtergraph(AVFilterGraph* graph,
     AVFilterInOut *outputs = nullptr, *inputs = nullptr;
 
     if (filtergraph) {
+        // 添加调试信息
+        qDebug("configure_filtergraph: filtergraph = %s", filtergraph);
+
         outputs = avfilter_inout_alloc();
         inputs = avfilter_inout_alloc();
         if (!outputs || !inputs) {
@@ -943,8 +1010,10 @@ int configure_filtergraph(AVFilterGraph* graph,
         inputs->pad_idx = 0;
         inputs->next = nullptr;
 
-        if ((ret = avfilter_graph_parse_ptr(graph, filtergraph, &inputs, &outputs, nullptr)) < 0)
+        if ((ret = avfilter_graph_parse_ptr(graph, filtergraph, &inputs, &outputs, nullptr)) < 0) {
+            qDebug("avfilter_graph_parse_ptr failed with error: %d", ret);
             goto fail;
+        }
     } else {
         if ((ret = avfilter_link(source_ctx, 0, sink_ctx, 0)) < 0)
             goto fail;

+ 2 - 1
AvPlayer2/packets_sync.h

@@ -9,7 +9,7 @@
 
 // only need to open audio filter, video will be synced
 #define USE_AVFILTER_AUDIO 1
-#define USE_AVFILTER_VIDEO 0
+#define USE_AVFILTER_VIDEO 1
 
 extern "C" {
 #include <libavcodec/avcodec.h>
@@ -437,6 +437,7 @@ int configure_filtergraph(AVFilterGraph* graph,
 // wanted_nb_channels, int wanted_sample_rate, struct AudioParams*
 // audio_hw_params);
 
+void set_video_watermark(VideoState* is, const char* watermark_text);
 void set_video_playspeed(VideoState* is);
 int configure_video_filters(AVFilterGraph* graph,
                             VideoState* is,

+ 40 - 25
AvPlayer2/video_decode_thread.cpp

@@ -23,10 +23,12 @@ void VideoDecodeThread::run()
     qCDebug(playerControllerVideoDecodeThread)
         << "[VideoDecodeThread] run start, m_pState:" << (void*) m_pState;
     assert(m_pState);
+
     VideoState* is = m_pState;
     qCDebug(playerControllerVideoDecodeThread)
         << "[VideoDecodeThread] VideoState* is:" << (void*) is
         << ", abort_request:" << is->abort_request;
+
     AVFrame* frame = av_frame_alloc();
     AVFrame* sw_frame = av_frame_alloc();
     AVFrame* tmp_frame = nullptr;
@@ -35,46 +37,59 @@ void VideoDecodeThread::run()
     int ret;
     AVRational tb = is->video_st->time_base;
     AVRational frame_rate = av_guess_frame_rate(is->ic, is->video_st, nullptr);
+
+#if USE_AVFILTER_VIDEO
+    // AVFilterGraph* graph = nullptr;
+    AVFilterContext *filt_out = nullptr, *filt_in = nullptr;
+    int last_w = 0;
+    int last_h = 0;
+    enum AVPixelFormat last_format = AV_PIX_FMT_NONE;
+    int last_serial = -1;
+    int last_vfilter_idx = 0;
+#endif
+
     if (!frame) {
-        qCWarning(playerControllerVideoDecodeThread)
-            << "[VideoDecodeThread] av_frame_alloc failed!";
+        // qCWarning(playerControllerVideoDecodeThread)
+        //     << "[VideoDecodeThread] av_frame_alloc failed!";
         return;
     }
-    int loop_count = 0;
+    // int loop_count = 0;
     for (;;) {
-        qCDebug(playerControllerVideoDecodeThread)
-            << "[VideoDecodeThread] loop start, loop_count:" << loop_count
-            << ", m_exit:" << (m_exit ? m_exit->load() : -1)
-            << ", abort_request:" << is->abort_request;
+        // qCDebug(playerControllerVideoDecodeThread)
+        //     << "[VideoDecodeThread] loop start, loop_count:" << loop_count
+        //     << ", m_exit:" << (m_exit ? m_exit->load() : -1)
+        //     << ", abort_request:" << is->abort_request;
         if (isExit()) {
-            qCDebug(playerControllerVideoDecodeThread) << "[VideoDecodeThread] m_exit set, exit.";
+            //qCDebug(playerControllerVideoDecodeThread) << "[VideoDecodeThread] m_exit set, exit.";
             break;
         }
         if (is->abort_request) {
-            qCDebug(playerControllerVideoDecodeThread)
-                << "[VideoDecodeThread] abort_request set, exit.";
+            // qCDebug(playerControllerVideoDecodeThread)
+            //     << "[VideoDecodeThread] abort_request set, exit.";
             break;
         }
-        qCDebug(playerControllerVideoDecodeThread)
-            << "[VideoDecodeThread] call get_video_frame, loop:" << loop_count;
+        // qCDebug(playerControllerVideoDecodeThread)
+        //     << "[VideoDecodeThread] call get_video_frame, loop:" << loop_count;
         ret = get_video_frame(is, frame);
-        qCDebug(playerControllerVideoDecodeThread)
-            << "[VideoDecodeThread] get_video_frame ret:" << ret << ", loop:" << loop_count++
-            << ", m_exit:" << (m_exit ? m_exit->load() : -1)
-            << ", abort_request:" << is->abort_request;
+        // qCDebug(playerControllerVideoDecodeThread)
+        //     << "[VideoDecodeThread] get_video_frame ret:" << ret << ", loop:" << loop_count++
+        //     << ", m_exit:" << (m_exit ? m_exit->load() : -1)
+        //     << ", abort_request:" << is->abort_request;
 
         if (ret < 0) {
-            qCWarning(playerControllerVideoDecodeThread)
-                << "[VideoDecodeThread] get_video_frame failed, exit.";
+            // qCWarning(playerControllerVideoDecodeThread)
+            //     << "[VideoDecodeThread] get_video_frame failed, exit.";
             goto the_end;
         }
         if (!ret) {
-            qCDebug(playerControllerVideoDecodeThread) << "[VideoDecodeThread] no frame, continue. m_exit:" << (m_exit ? m_exit->load() : -1) << ", abort_request:" << is->abort_request;
+            // qCDebug(playerControllerVideoDecodeThread)
+            //     << "[VideoDecodeThread] no frame, continue. m_exit:"
+            //     << (m_exit ? m_exit->load() : -1) << ", abort_request:" << is->abort_request;
             continue;
         }
-        qCDebug(playerControllerVideoDecodeThread)
-            << "[VideoDecodeThread] got video frame, pts:" << frame->pts
-            << ", width:" << frame->width << ", height:" << frame->height;
+        // qCDebug(playerControllerVideoDecodeThread)
+        //     << "[VideoDecodeThread] got video frame, pts:" << frame->pts
+        //     << ", width:" << frame->width << ", height:" << frame->height;
 
 #if USE_AVFILTER_VIDEO
         if (last_w != frame->width || last_h != frame->height || last_format != frame->format
@@ -188,8 +203,8 @@ the_end:
     av_frame_free(&frame);
     av_frame_free(&sw_frame);
     // 可加日志输出
-    qCDebug(playerControllerVideoDecodeThread)
-        << "[VideoDecodeThread] run end, abort_request:" << is->abort_request
-        << ", m_exit:" << (m_exit ? m_exit->load() : -1);
+    // qCDebug(playerControllerVideoDecodeThread)
+    //     << "[VideoDecodeThread] run end, abort_request:" << is->abort_request
+    //     << ", m_exit:" << (m_exit ? m_exit->load() : -1);
     return;
 }

+ 18 - 10
AvPlayer2/video_play_thread.cpp

@@ -34,42 +34,42 @@ VideoPlayThread::~VideoPlayThread()
 
 void VideoPlayThread::run()
 {
-    qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] run start, m_pState:" << (void*)m_pState;
+    //qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] run start, m_pState:" << (void*)m_pState;
     assert(m_pState);
     VideoState* is = m_pState;
-    qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] VideoState* is:" << (void*)is << ", abort_request:" << is->abort_request;
+    // qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] VideoState* is:" << (void*)is << ", abort_request:" << is->abort_request;
     double remaining_time = 0.0;
     int loop_count = 0;
     for (;;) {
         if (isExit()) {
-            qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] m_exit set, exit.";
+            // qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] m_exit set, exit.";
             break;
         }
         if (is->abort_request) {
-            qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] abort_request set, exit.";
+            // qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] abort_request set, exit.";
             break;
         }
         if (is->eof && frame_queue_nb_remaining(&is->pictq) == 0) {
-            qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] eof且队列空,退出线程";
+            //  qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] eof且队列空,退出线程";
             break; // 线程退出
         }
         if (is->paused) {
-            qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] paused, wait.";
+            // qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] paused, wait.";
             std::unique_lock<std::mutex> lock(m_mutex);
             m_cv.wait_for(lock, std::chrono::milliseconds(10), [this] { return isExit(); });
             continue;
         }
-        qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] call video_refresh, loop:" << loop_count << ", pictq size:" << (is ? is->pictq.size : -1) << ", remaining_time:" << remaining_time;
+        // qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] call video_refresh, loop:" << loop_count << ", pictq size:" << (is ? is->pictq.size : -1) << ", remaining_time:" << remaining_time;
         if (remaining_time > 0.0)
             av_usleep((int64_t) (remaining_time * 1000000.0));
         remaining_time = REFRESH_RATE;
         if ((!is->paused || is->force_refresh)) {
-            qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] call video_refresh, loop:" << loop_count++;
+            // qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] call video_refresh, loop:" << loop_count++;
             video_refresh(is, &remaining_time);
         }
     }
-    qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] run end, abort_request:" << is->abort_request
-                                 << ", m_exit:" << (m_exit ? m_exit->load() : -1);
+    //qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] run end, abort_request:" << is->abort_request
+    //                             << ", m_exit:" << (m_exit ? m_exit->load() : -1);
 }
 
 void VideoPlayThread::video_refresh(VideoState* is, double* remaining_time)
@@ -79,6 +79,14 @@ void VideoPlayThread::video_refresh(VideoState* is, double* remaining_time)
 
     if (!is->paused && get_master_sync_type(is) == AV_SYNC_EXTERNAL_CLOCK && is->realtime)
         check_external_clock_speed(is);
+    
+    // 定期更新水印显示的时间信息(每秒更新一次)
+    static double last_watermark_update = 0.0;
+    double current_time_sec = av_gettime_relative() / 1000000.0;
+    if (current_time_sec - last_watermark_update >= 1.0) {
+        set_video_watermark(is, "Time");
+        last_watermark_update = current_time_sec;
+    }
 
     if (is->video_st) {
     retry:

+ 3 - 0
AvPlayer2/video_state.cpp

@@ -639,6 +639,9 @@ int VideoStateData::stream_component_open(VideoState* is, int stream_index)
         is->video_stream = stream_index;
         is->video_st = ic->streams[stream_index];
 
+        // 设置视频水印
+        set_video_watermark(is, "Watermark");
+
         m_bHasVideo = true;
         m_avctxVideo = avctx;
         break;

+ 2 - 2
main.cpp

@@ -1,5 +1,4 @@
 #include "AVPlayer2/mainwindowa.h"
-#include "AvPlayer/playerdemowindow.h"
 #include "mainwindow.h"
 #include "thememanager.h"
 #include "themesettingswidget.h"
@@ -52,7 +51,8 @@ void myMessageHandler(QtMsgType type, const QMessageLogContext &context, const Q
 namespace avrecorder::video { void InitWinRTCapture(); }
 int main(int argc, char* argv[])
 {
-    // QLoggingCategory::setFilterRules(QStringLiteral("player.controller.*.debug=false\n"));
+    QLoggingCategory::setFilterRules(QStringLiteral("player.controller.ReadThread=false\n"
+                                                    "player.controller.AudioPlayThread=false\n"));
     // QLoggingCategory::setFilterRules("*.debug=false\n"
     //                                  "*.info=false\n"
     //                                  "*.warning=false\n"