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 }