File indexing completed on 2024-06-16 04:38:30

0001 /*
0002     SPDX-FileCopyrightText: 2003 Fabrice Bellard
0003     SPDX-FileCopyrightText: 2020-2022 Mladen Milinkovic <max@smoothware.net>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "renderthread.h"
0009 
0010 #include <QMutex>
0011 
0012 #include "videoplayer/backend/videostate.h"
0013 #include "videoplayer/backend/glrenderer.h"
0014 
0015 extern "C" {
0016 #include "libavutil/time.h"
0017 #include "libavformat/avformat.h"
0018 #include "libavutil/pixdesc.h"
0019 }
0020 
0021 
0022 using namespace SubtitleComposer;
0023 
0024 RenderThread::RenderThread(VideoState *state, QObject *parent)
0025     : QThread(parent),
0026       m_vs(state)
0027 {
0028 }
0029 
0030 void
0031 RenderThread::run()
0032 {
0033     double remaining_time = 0.0;
0034     for(;;) {
0035         if(remaining_time > 0.0)
0036             av_usleep((int64_t)(remaining_time * double(AV_TIME_BASE)));
0037         else
0038             yieldCurrentThread(); // allow gui to update
0039         remaining_time = REFRESH_RATE;
0040         if(isInterruptionRequested())
0041             break;
0042         if(m_vs->showMode != SHOW_MODE_NONE && (!m_vs->paused || m_vs->forceRefresh))
0043             videoRefresh(&remaining_time);
0044     }
0045 }
0046 
0047 // called to display each frame
0048 void
0049 RenderThread::videoRefresh(double *remainingTime)
0050 {
0051     double time = 0.0;
0052 
0053     if(!m_vs->paused && m_vs->masterSyncType() == AV_SYNC_EXTERNAL_CLOCK && m_vs->realTime)
0054         m_vs->checkExternalClockSpeed();
0055 
0056 #ifdef AUDIO_VISUALIZATION
0057     if(m_vs->showMode != SHOW_MODE_VIDEO && m_vs->audStream) {
0058         time = av_gettime_relative() / double(AV_TIME_BASE);
0059         if(m_vs->forceRefresh || m_vs->last_vis_time + m_vs->rdftspeed < time) {
0060             videoDisplay();
0061             m_vs->last_vis_time = time;
0062         }
0063         *remainingTime = FFMIN(*remainingTime, m_vs->last_vis_time + m_vs->rdftspeed - time);
0064     }
0065 #endif
0066 
0067     if(m_vs->vidStream) {
0068 retry:
0069         if(m_vs->vidFQ.nbRemaining() == 0) {
0070             // nothing to do, no picture to display in the queue
0071         } else {
0072             double last_duration, duration, delay;
0073             Frame *vp, *lastvp;
0074 
0075             /* dequeue the picture */
0076             lastvp = m_vs->vidFQ.peekLast();
0077             vp = m_vs->vidFQ.peek();
0078 
0079             if(vp->serial != m_vs->vidPQ.serial()) {
0080                 m_vs->vidFQ.next();
0081                 goto retry;
0082             }
0083 
0084             if(lastvp->serial != vp->serial)
0085                 m_vs->frameTimer = av_gettime_relative() / double(AV_TIME_BASE);
0086 
0087             if(m_vs->paused)
0088                 goto display;
0089 
0090             // compute nominal last_duration
0091             last_duration = vpDuration(lastvp, vp);
0092             delay = computeTargetDelay(last_duration);
0093 
0094             time = av_gettime_relative() / double(AV_TIME_BASE);
0095             if(time < m_vs->frameTimer + delay) {
0096                 *remainingTime = FFMIN(m_vs->frameTimer + delay - time, *remainingTime);
0097                 goto display;
0098             }
0099 
0100             m_vs->frameTimer += delay;
0101             if(delay > 0 && time - m_vs->frameTimer > AV_SYNC_THRESHOLD_MAX)
0102                 m_vs->frameTimer = time;
0103 
0104             m_vs->vidFQ.m_mutex->lock();
0105             if(!std::isnan(vp->pts))
0106                 updateVideoPts(vp->pts, vp->pos, vp->serial);
0107             m_vs->vidFQ.m_mutex->unlock();
0108 
0109             if(m_vs->vidFQ.nbRemaining() > 1) {
0110                 Frame *nextvp = m_vs->vidFQ.peekNext();
0111                 duration = vpDuration(vp, nextvp);
0112                 if(!m_vs->step
0113                 && (m_vs->framedrop > 0 || (m_vs->framedrop && m_vs->masterSyncType() != AV_SYNC_VIDEO_MASTER))
0114                 && time > m_vs->frameTimer + duration) {
0115                     m_vs->frameDropsLate++;
0116                     m_vs->vidFQ.next();
0117                     goto retry;
0118                 }
0119             }
0120 
0121 #ifdef VIDEO_SUBTITLE
0122             if(m_vs->subStream) {
0123                 while(m_vs->subFQ.nbRemaining() > 0) {
0124                     Frame *sp = m_vs->subFQ.peek();
0125                     Frame *sp2 = m_vs->subFQ.nbRemaining() > 1 ? m_vs->subFQ.peekNext() : nullptr;
0126 
0127                     if(sp->serial != m_vs->subPQ.serial()
0128                        || (m_vs->vidClk.pts() > (sp->pts + ((float)sp->sub.end_display_time / 1000)))
0129                        || (sp2 && m_vs->vidClk.pts() > (sp2->pts + ((float)sp2->sub.start_display_time / 1000)))) {
0130                         if(sp->uploaded) {
0131                             int i;
0132                             for(i = 0; i < (int)sp->sub.num_rects; i++) {
0133                                 AVSubtitleRect *sub_rect = sp->sub.rects[i];
0134                                 uint8_t *pixels;
0135                                 int pitch, j;
0136 
0137                                 if(!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)&pixels, &pitch)) {
0138                                     for(j = 0; j < sub_rect->h; j++, pixels += pitch)
0139                                         memset(pixels, 0, sub_rect->w << 2);
0140                                     SDL_UnlockTexture(is->sub_texture);
0141                                 }
0142                             }
0143                         }
0144                         m_vs->subFQ.next();
0145                     } else {
0146                         break;
0147                     }
0148                 }
0149             }
0150 #endif
0151 
0152             m_vs->vidFQ.next();
0153             m_vs->forceRefresh = true;
0154 
0155             if(m_vs->step && !m_vs->paused)
0156                 m_vs->demuxer->pauseToggle();
0157         }
0158 display:
0159         // display picture
0160         if(m_vs->forceRefresh && m_vs->showMode == SHOW_MODE_VIDEO && m_vs->vidFQ.m_rIndexShown)
0161             videoDisplay();
0162     }
0163     m_vs->forceRefresh = false;
0164 }
0165 
0166 void
0167 RenderThread::videoDisplay()
0168 {
0169     if(m_vs->audStream && m_vs->showMode != SHOW_MODE_VIDEO)
0170         videoAudioDisplay();
0171     else if(m_vs->vidStream)
0172         videoImageDisplay();
0173 }
0174 
0175 double
0176 RenderThread::vpDuration(Frame *vp, Frame *nextvp)
0177 {
0178     if(vp->serial == nextvp->serial) {
0179         double duration = nextvp->pts - vp->pts;
0180         if(std::isnan(duration) || duration <= 0 || duration > m_vs->maxFrameDuration)
0181             return vp->duration;
0182         else
0183             return duration;
0184     } else {
0185         return 0.0;
0186     }
0187 }
0188 
0189 void
0190 RenderThread::updateVideoPts(double pts, int64_t /*pos*/, int serial)
0191 {
0192     // update current video pts
0193     m_vs->vidClk.set(pts, serial);
0194     m_vs->extClk.syncTo(&m_vs->vidClk);
0195 }
0196 
0197 double
0198 RenderThread::computeTargetDelay(double delay)
0199 {
0200     double sync_threshold, diff = 0;
0201 
0202     // update delay to follow master synchronisation source
0203     if(m_vs->masterSyncType() != AV_SYNC_VIDEO_MASTER) {
0204         // if video is slave, we try to correct big delays by duplicating or deleting a frame
0205         diff = m_vs->vidClk.get() - m_vs->masterTime();
0206 
0207         // skip or repeat frame. We take into account the delay to compute the threshold.
0208         // I still don't know if it is the best guess
0209         sync_threshold = FFMAX(AV_SYNC_THRESHOLD_MIN, FFMIN(AV_SYNC_THRESHOLD_MAX, delay));
0210         if(!std::isnan(diff) && fabs(diff) < m_vs->maxFrameDuration) {
0211             if(diff <= -sync_threshold)
0212                 delay = FFMAX(0, delay + diff);
0213             else if(diff >= sync_threshold && delay > AV_SYNC_FRAMEDUP_THRESHOLD)
0214                 delay = delay + diff;
0215             else if(diff >= sync_threshold)
0216                 delay = 2 * delay;
0217         }
0218     }
0219 
0220     av_log(nullptr, AV_LOG_TRACE, "video: delay=%0.3f A-V=%f\n", delay, -diff);
0221 
0222     return delay;
0223 }
0224 
0225 void
0226 RenderThread::videoAudioDisplay()
0227 {
0228 #ifdef AUDIO_VISUALIZATION
0229     int i, i_start, x, y1, y, ys, delay, n, nb_display_channels;
0230     int ch, channels, h, h2;
0231     int64_t time_diff;
0232     int rdft_bits, nb_freq;
0233 
0234     for(rdft_bits = 1; (1 << rdft_bits) < 2 * m_vs->height; rdft_bits++);
0235     nb_freq = 1 << (rdft_bits - 1);
0236 
0237     // compute display index : center on currently output samples
0238     channels = m_vs->audio_tgt.channels;
0239     nb_display_channels = channels;
0240     if(!m_vs->paused) {
0241         int data_used = m_vs->show_mode == SHOW_MODE_WAVES ? m_vs->width : (2 * nb_freq);
0242         n = 2 * channels;
0243         delay = m_vs->audio_write_buf_size;
0244         delay /= n;
0245 
0246         // to be more precise, we take into account the time spent since the last buffer computation
0247         if(ffplay::audio_callback_time) {
0248             time_diff = av_gettime_relative() - ffplay::audio_callback_time;
0249             delay -= (time_diff * m_vs->audio_tgt.freq) / 1000000;
0250         }
0251 
0252         delay += 2 * data_used;
0253         if(delay < data_used)
0254             delay = data_used;
0255 
0256         i_start = x = compute_mod(m_vs->sample_array_index - delay * channels, SAMPLE_ARRAY_SIZE);
0257         if(m_vs->show_mode == SHOW_MODE_WAVES) {
0258             h = INT_MIN;
0259             for(i = 0; i < 1000; i += channels) {
0260                 int idx = (SAMPLE_ARRAY_SIZE + x - i) % SAMPLE_ARRAY_SIZE;
0261                 int a = m_vs->sample_array[idx];
0262                 int b = m_vs->sample_array[(idx + 4 * channels) % SAMPLE_ARRAY_SIZE];
0263                 int c = m_vs->sample_array[(idx + 5 * channels) % SAMPLE_ARRAY_SIZE];
0264                 int d = m_vs->sample_array[(idx + 9 * channels) % SAMPLE_ARRAY_SIZE];
0265                 int score = a - d;
0266                 if(h < score && (b ^ c) < 0) {
0267                     h = score;
0268                     i_start = idx;
0269                 }
0270             }
0271         }
0272 
0273         m_vs->last_i_start = i_start;
0274     } else {
0275         i_start = m_vs->last_i_start;
0276     }
0277 
0278     if(m_vs->show_mode == SHOW_MODE_WAVES) {
0279 //      SDL_SetRenderDrawColor(ffplay::renderer, 255, 255, 255, 255);
0280 
0281         // total height for one channel
0282         h = m_vs->height / nb_display_channels;
0283         // graph height / 2
0284         h2 = (h * 9) / 20;
0285         for(ch = 0; ch < nb_display_channels; ch++) {
0286             i = i_start + ch;
0287             y1 = m_vs->ytop + ch * h + (h / 2); // position of center line
0288             for(x = 0; x < m_vs->width; x++) {
0289                 y = (m_vs->sample_array[i] * h2) >> 15;
0290                 if(y < 0) {
0291                     y = -y;
0292                     ys = y1 - y;
0293                 } else {
0294                     ys = y1;
0295                 }
0296                 fill_rectangle(m_vs->xleft + x, ys, 1, y);
0297                 i += channels;
0298                 if(i >= SAMPLE_ARRAY_SIZE)
0299                     i -= SAMPLE_ARRAY_SIZE;
0300             }
0301         }
0302 
0303 //      SDL_SetRenderDrawColor(ffplay::renderer, 0, 0, 255, 255);
0304 
0305         for(ch = 1; ch < nb_display_channels; ch++) {
0306             y = m_vs->ytop + ch * h;
0307             fill_rectangle(m_vs->xleft, y, m_vs->width, 1);
0308         }
0309     } else {
0310         nb_display_channels = FFMIN(nb_display_channels, 2);
0311         if(rdft_bits != m_vs->rdft_bits) {
0312             av_rdft_end(m_vs->rdft);
0313             av_free(m_vs->rdft_data);
0314             m_vs->rdft = av_rdft_init(rdft_bits, DFT_R2C);
0315             m_vs->rdft_bits = rdft_bits;
0316             m_vs->rdft_data = (FFTSample *)av_malloc_array(nb_freq, 4 * sizeof(*m_vs->rdft_data));
0317         }
0318         if(!m_vs->rdft || !m_vs->rdft_data) {
0319             av_log(nullptr, AV_LOG_ERROR, "Failed to allocate buffers for RDFT, switching to waves display\n");
0320             m_vs->show_mode = SHOW_MODE_WAVES;
0321         } else {
0322             FFTSample *data[2];
0323             for(ch = 0; ch < nb_display_channels; ch++) {
0324                 data[ch] = m_vs->rdft_data + 2 * nb_freq * ch;
0325                 i = i_start + ch;
0326                 for(x = 0; x < 2 * nb_freq; x++) {
0327                     double w = (x - nb_freq) * (1.0 / nb_freq);
0328                     data[ch][x] = m_vs->sample_array[i] * (1.0 - w * w);
0329                     i += channels;
0330                     if(i >= SAMPLE_ARRAY_SIZE)
0331                         i -= SAMPLE_ARRAY_SIZE;
0332                 }
0333                 av_rdft_calc(m_vs->rdft, data[ch]);
0334             }
0335             // Least efficient way to do this, we should of course
0336             // directly access it but it is more than fast enough.
0337             SDL_Rect rect = { .x = m_vs->xpos, .y = 0, .w = 1, .h = m_vs->height };
0338             uint32_t *pixels;
0339             int pitch;
0340             if(!SDL_LockTexture(m_vs->vis_texture, &rect, (void **)&pixels, &pitch)) {
0341                 pitch >>= 2;
0342                 pixels += pitch * m_vs->height;
0343                 for(y = 0; y < m_vs->height; y++) {
0344                     double w = 1 / sqrt(nb_freq);
0345                     int a = sqrt(
0346                         w * sqrt(data[0][2 * y + 0] * data[0][2 * y + 0] + data[0][2 * y + 1] * data[0][2 * y + 1]));
0347                     int b = (nb_display_channels == 2) ? sqrt(w * hypot(data[1][2 * y + 0], data[1][2 * y + 1]))
0348                                                        : a;
0349                     a = FFMIN(a, 255);
0350                     b = FFMIN(b, 255);
0351                     pixels -= pitch;
0352                     *pixels = (a << 16) + (b << 8) + ((a + b) >> 1);
0353                 }
0354                 SDL_UnlockTexture(m_vs->vis_texture);
0355             }
0356             SDL_RenderCopy(ffplay::renderer, m_vs->vis_texture, nullptr, nullptr);
0357         }
0358         if(!m_vs->paused)
0359             m_vs->xpos++;
0360         if(m_vs->xpos >= m_vs->width)
0361             m_vs->xpos = m_vs->xleft;
0362     }
0363 #endif
0364 }
0365 
0366 void
0367 RenderThread::videoImageDisplay()
0368 {
0369     Frame *vp = m_vs->vidFQ.peekLast();
0370 #ifdef VIDEO_SUBTITLE
0371     Frame *sp = nullptr;
0372     if(m_vs->subStream) {
0373         if(m_vs->subFQ.nbRemaining() > 0) {
0374             sp = m_vs->subFQ.peek();
0375 
0376             if(vp->pts >= sp->pts + ((float)sp->sub.start_display_time / 1000)) {
0377                 if(!sp->uploaded) {
0378                     uint8_t *pixels[4];
0379                     int pitch[4];
0380                     int i;
0381                     if(!sp->width || !sp->height) {
0382                         sp->width = vp->width;
0383                         sp->height = vp->height;
0384                     }
0385 
0386                     for(i = 0; i < (int)sp->sub.num_rects; i++) {
0387                         AVSubtitleRect *sub_rect = sp->sub.rects[i];
0388 
0389                         sub_rect->x = av_clip(sub_rect->x, 0, sp->width);
0390                         sub_rect->y = av_clip(sub_rect->y, 0, sp->height);
0391                         sub_rect->w = av_clip(sub_rect->w, 0, sp->width - sub_rect->x);
0392                         sub_rect->h = av_clip(sub_rect->h, 0, sp->height - sub_rect->y);
0393 
0394                         m_vs->subConvertCtx = sws_getCachedContext(m_vs->subConvertCtx,
0395                                 sub_rect->w, sub_rect->h, AV_PIX_FMT_PAL8,
0396                                 sub_rect->w, sub_rect->h, AV_PIX_FMT_BGRA,
0397                                 0, nullptr, nullptr, nullptr);
0398                         if(!m_vs->subConvertCtx) {
0399                             av_log(nullptr, AV_LOG_FATAL, "Cannot initialize the conversion context\n");
0400                             return;
0401                         }
0402                         if(!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)pixels, pitch)) {
0403                             sws_scale(is->sub_convert_ctx, (const uint8_t *const *)sub_rect->data, sub_rect->linesize, 0, sub_rect->h, pixels, pitch);
0404                             SDL_UnlockTexture(is->sub_texture);
0405                         }
0406                     }
0407                     sp->uploaded = 1;
0408                 }
0409             } else
0410                 sp = nullptr;
0411         }
0412     }
0413 #endif
0414 
0415     if(!vp->uploaded) {
0416         if(m_vs->glRenderer->uploadTexture(vp->frame) < 0) {
0417             requestInterruption();
0418             return;
0419         }
0420         vp->uploaded = true;
0421     }
0422 
0423 #ifdef VIDEO_SUBTITLE
0424     if(sp) {
0425 #if USE_ONEPASS_SUBTITLE_RENDER
0426 //      SDL_RenderCopy(ffplay::renderer, is->sub_texture, nullptr, &rect);
0427 #else
0428         int i;
0429         double xratio = (double)rect.w / (double)sp->width;
0430         double yratio = (double)rect.h / (double)sp->height;
0431         for(i = 0; i < sp->sub.num_rects; i++) {
0432             SDL_Rect *sub_rect = (SDL_Rect*)sp->sub.rects[i];
0433             SDL_Rect target = {.x = rect.x + sub_rect->x * xratio,
0434                                .y = rect.y + sub_rect->y * yratio,
0435                                .w = sub_rect->w * xratio,
0436                                .h = sub_rect->h * yratio};
0437             SDL_RenderCopy(renderer, is->sub_texture, sub_rect, &target);
0438         }
0439 #endif
0440     }
0441 #endif
0442 }
0443 
0444 // copy samples for viewing in editor window
0445 void
0446 RenderThread::updateSampleDisplay(short *samples, int samplesSize)
0447 {
0448 #ifdef AUDIO_VISUALIZATION
0449     int size = samplesSize / sizeof(int16_t);
0450     while(size > 0) {
0451         int len = m_vs->sample_array.capacity() - m_vs->sample_array_index;
0452         if(len > size)
0453             len = size;
0454         memcpy(&m_vs->sample_array[m_vs->sample_array_index], samples, len * sizeof(int16_t));
0455         samples += len;
0456         m_vs->sample_array_index += len;
0457         if(m_vs->sample_array_index >= m_vs->sample_array.capacity())
0458             m_vs->sample_array_index = 0;
0459         size -= len;
0460     }
0461 #else
0462     Q_UNUSED(samples)
0463     Q_UNUSED(samplesSize)
0464 #endif
0465 }
0466 
0467 void
0468 RenderThread::toggleAudioDisplay()
0469 {
0470     int next = m_vs->showMode;
0471     do {
0472         next = (next + 1) % SHOW_MODE_NB;
0473     } while((next != m_vs->showMode && next == SHOW_MODE_VIDEO && !m_vs->vidStream) || (next != SHOW_MODE_VIDEO && !m_vs->audStream));
0474     if(m_vs->showMode != next) {
0475         m_vs->forceRefresh = true;
0476         m_vs->showMode = ShowMode(next);
0477     }
0478 }