| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569 |
- #include "video_play_thread.h"
- #include <QLoggingCategory>
- #include "AVPlayer2/playercontroller.h"
- Q_LOGGING_CATEGORY(playerControllerVideoPlayThread, "player.controller.VideoPlayThread")
- extern int framedrop;
- const QRegularExpression VideoPlayThread::m_assFilter = QRegularExpression("{\\\\.*?}");
- const QRegularExpression VideoPlayThread::m_assNewLineReplacer = QRegularExpression("\\\\n|\\\\N");
- Q_DECLARE_METATYPE(AVFrame*)
- VideoPlayThread::VideoPlayThread(VideoState* pState)
- : m_pState(pState)
- {
- qRegisterMetaType<AVFrame*>();
- }
- VideoPlayThread::~VideoPlayThread()
- {
- final_resample_param();
- }
- void VideoPlayThread::run()
- {
- //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;
- double remaining_time = 0.0;
- int loop_count = 0;
- for (;;) {
- if (isExit()) {
- // qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] m_exit set, exit.";
- break;
- }
- if (is->abort_request) {
- // qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] abort_request set, exit.";
- break;
- }
- if (is->eof && frame_queue_nb_remaining(&is->pictq) == 0) {
- // qCDebug(playerControllerVideoPlayThread) << "[VideoPlayThread] eof且队列空,退出线程";
- break; // 线程退出
- }
- if (is->paused) {
- // 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;
- 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++;
- video_refresh(is, &remaining_time);
- }
- }
- //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)
- {
- double time;
- Frame *sp, *sp2;
- 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:
- // ffplay方案:eof且只剩最后一帧时,消耗掉最后一帧
- if (is->eof && frame_queue_nb_remaining(&is->pictq) == 1) {
- frame_queue_next(&is->pictq);
- // 这里可以考虑发信号通知播放结束
- return;
- }
- if (frame_queue_nb_remaining(&is->pictq) == 0) {
- // nothing to do, no picture to display in the queue
- *remaining_time = REFRESH_RATE;
- } else {
- double last_duration, duration, delay;
- Frame *vp, *lastvp;
- /* dequeue the picture */
- lastvp = frame_queue_peek_last(&is->pictq);
- vp = frame_queue_peek(&is->pictq);
- if (vp->serial != is->videoq.serial) {
- frame_queue_next(&is->pictq);
- goto retry;
- }
- if (lastvp->serial != vp->serial)
- is->frame_timer = av_gettime_relative() / 1000000.0;
- if (is->paused)
- goto display;
- /* compute nominal last_duration */
- last_duration = vp_duration(is, lastvp, vp);
- delay = compute_target_delay(last_duration, is);
- time = av_gettime_relative() / 1000000.0;
- if (time < is->frame_timer + delay) {
- *remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time);
- goto display;
- }
- is->frame_timer += delay;
- if (delay > 0 && time - is->frame_timer > AV_SYNC_THRESHOLD_MAX)
- is->frame_timer = time;
- is->pictq.mutex->lock();
- if (!isnan(vp->pts))
- update_video_pts(is, vp->pts, vp->pos, vp->serial);
- is->pictq.mutex->unlock();
- if (frame_queue_nb_remaining(&is->pictq) > 1) {
- Frame* nextvp = frame_queue_peek_next(&is->pictq);
- duration = vp_duration(is, vp, nextvp);
- if (!is->step
- && (framedrop > 0
- || (framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER))
- && time > is->frame_timer + duration) {
- is->frame_drops_late++;
- frame_queue_next(&is->pictq);
- goto retry;
- }
- }
- if (is->subtitle_st) {
- while (frame_queue_nb_remaining(&is->subpq) > 0) {
- sp = frame_queue_peek(&is->subpq);
- if (frame_queue_nb_remaining(&is->subpq) > 1)
- sp2 = frame_queue_peek_next(&is->subpq);
- else
- sp2 = nullptr;
- if (sp->serial != is->subtitleq.serial
- || (is->vidclk.pts > (sp->pts + ((float) sp->sub.end_display_time / 1000)))
- || (sp2
- && is->vidclk.pts
- > (sp2->pts + ((float) sp2->sub.start_display_time / 1000)))) {
- #if 0
- if (sp->uploaded) {
- int i;
- for (i = 0; i < sp->sub.num_rects; i++) {
- AVSubtitleRect* sub_rect = sp->sub.rects[i];
- /*uint8_t* pixels;
- int pitch, j;
- if (!SDL_LockTexture(is->sub_texture, (SDL_Rect*)sub_rect, (void**)&pixels, &pitch)) {
- for (j = 0; j < sub_rect->h; j++, pixels += pitch)
- memset(pixels, 0, sub_rect->w << 2);
- SDL_UnlockTexture(is->sub_texture);
- }*/
- }
- }
- #endif
- frame_queue_next(&is->subpq);
- } else {
- break;
- }
- }
- }
- frame_queue_next(&is->pictq);
- is->force_refresh = 1;
- if (is->step && !is->paused)
- toggle_pause(is, !is->step);
- }
- display:
- /* display picture */
- if (is->force_refresh && is->pictq.rindex_shown)
- video_display(is);
- }
- is->force_refresh = 0;
- }
- void VideoPlayThread::video_display(VideoState* is)
- {
- if (is->audio_st && false) {
- // video_audio_display(is);
- } else if (is->video_st) {
- video_image_display(is);
- }
- }
- #if 0
- void VideoPlayThread::video_audio_display(VideoState* s)
- {
- int64_t audio_callback_time = 0;
- int i, i_start, x, y1, y, ys, delay, n, nb_display_channels;
- int ch, channels, h, h2;
- int64_t time_diff;
- int rdft_bits, nb_freq;
- for (rdft_bits = 1; (1 << rdft_bits) < 2 * s->height; rdft_bits++)
- ;
- nb_freq = 1 << (rdft_bits - 1);
- /* compute display index : center on currently output samples */
- channels = s->audio_tgt.channels;
- nb_display_channels = channels;
- if (!s->paused) {
- int data_used = (2 * nb_freq);
- n = 2 * channels;
- delay = s->audio_write_buf_size;
- delay /= n;
- /* to be more precise, we take into account the time spent since
- the last buffer computation */
- if (audio_callback_time) {
- time_diff = av_gettime_relative() - audio_callback_time;
- delay -= (time_diff * s->audio_tgt.freq) / 1000000;
- }
- delay += 2 * data_used;
- if (delay < data_used)
- delay = data_used;
- i_start = x = compute_mod(s->sample_array_index - delay * channels, SAMPLE_ARRAY_SIZE);
- /*if (s->show_mode == SHOW_MODE_WAVES) {
- h = INT_MIN;
- for (i = 0; i < 1000; i += channels) {
- int idx = (SAMPLE_ARRAY_SIZE + x - i) % SAMPLE_ARRAY_SIZE;
- int a = s->sample_array[idx];
- int b = s->sample_array[(idx + 4 * channels) % SAMPLE_ARRAY_SIZE];
- int c = s->sample_array[(idx + 5 * channels) % SAMPLE_ARRAY_SIZE];
- int d = s->sample_array[(idx + 9 * channels) % SAMPLE_ARRAY_SIZE];
- int score = a - d;
- if (h < score && (b ^ c) < 0) {
- h = score;
- i_start = idx;
- }
- }
- }*/
- s->last_i_start = i_start;
- }
- else {
- i_start = s->last_i_start;
- }
- #if 0
- if (s->show_mode == SHOW_MODE_WAVES) {
- SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
- /* total height for one channel */
- h = s->height / nb_display_channels;
- /* graph height / 2 */
- h2 = (h * 9) / 20;
- for (ch = 0; ch < nb_display_channels; ch++) {
- i = i_start + ch;
- y1 = s->ytop + ch * h + (h / 2); /* position of center line */
- for (x = 0; x < s->width; x++) {
- y = (s->sample_array[i] * h2) >> 15;
- if (y < 0) {
- y = -y;
- ys = y1 - y;
- }
- else {
- ys = y1;
- }
- fill_rectangle(s->xleft + x, ys, 1, y);
- i += channels;
- if (i >= SAMPLE_ARRAY_SIZE)
- i -= SAMPLE_ARRAY_SIZE;
- }
- }
- SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
- for (ch = 1; ch < nb_display_channels; ch++) {
- y = s->ytop + ch * h;
- fill_rectangle(s->xleft, y, s->width, 1);
- }
- }
- else {
- if (realloc_texture(&s->vis_texture, SDL_PIXELFORMAT_ARGB8888, s->width, s->height, SDL_BLENDMODE_NONE, 1) < 0)
- return;
- if (s->xpos >= s->width)
- s->xpos = 0;
- nb_display_channels = FFMIN(nb_display_channels, 2);
- if (rdft_bits != s->rdft_bits) {
- av_rdft_end(s->rdft);
- av_free(s->rdft_data);
- s->rdft = av_rdft_init(rdft_bits, DFT_R2C);
- s->rdft_bits = rdft_bits;
- s->rdft_data = av_malloc_array(nb_freq, 4 * sizeof(*s->rdft_data));
- }
- if (!s->rdft || !s->rdft_data) {
- av_log(nullptr, AV_LOG_ERROR, "Failed to allocate buffers for RDFT, switching to waves display\n");
- s->show_mode = SHOW_MODE_WAVES;
- }
- else {
- FFTSample* data[2];
- SDL_Rect rect = { .x = s->xpos, .y = 0, .w = 1, .h = s->height };
- uint32_t* pixels;
- int pitch;
- for (ch = 0; ch < nb_display_channels; ch++) {
- data[ch] = s->rdft_data + 2 * nb_freq * ch;
- i = i_start + ch;
- for (x = 0; x < 2 * nb_freq; x++) {
- double w = (x - nb_freq) * (1.0 / nb_freq);
- data[ch][x] = s->sample_array[i] * (1.0 - w * w);
- i += channels;
- if (i >= SAMPLE_ARRAY_SIZE)
- i -= SAMPLE_ARRAY_SIZE;
- }
- av_rdft_calc(s->rdft, data[ch]);
- }
- /* Least efficient way to do this, we should of course
- * directly access it but it is more than fast enough. */
- if (!SDL_LockTexture(s->vis_texture, &rect, (void**)&pixels, &pitch)) {
- pitch >>= 2;
- pixels += pitch * s->height;
- for (y = 0; y < s->height; y++) {
- double w = 1 / sqrt(nb_freq);
- int a = sqrt(w * sqrt(data[0][2 * y + 0] * data[0][2 * y + 0] + data[0][2 * y + 1] * data[0][2 * y + 1]));
- int b = (nb_display_channels == 2) ? sqrt(w * hypot(data[1][2 * y + 0], data[1][2 * y + 1]))
- : a;
- a = FFMIN(a, 255);
- b = FFMIN(b, 255);
- pixels -= pitch;
- *pixels = (a << 16) + (b << 8) + ((a + b) >> 1);
- }
- SDL_UnlockTexture(s->vis_texture);
- }
- SDL_RenderCopy(renderer, s->vis_texture, nullptr, nullptr);
- }
- if (!s->paused)
- s->xpos++;
- }
- #endif
- }
- #endif
- void VideoPlayThread::video_image_display(VideoState* is)
- {
- Frame* sp = nullptr;
- Frame* vp = frame_queue_peek_last(&is->pictq);
- Video_Resample* pResample = &m_Resample;
- if (frame_queue_nb_remaining(&is->subpq) > 0) {
- sp = frame_queue_peek(&is->subpq);
- if (vp->pts >= sp->pts + ((float) sp->sub.start_display_time / 1000)) {
- if (!sp->uploaded) {
- // uint8_t* pixels[4];
- // int pitch[4];
- if (!sp->width || !sp->height) {
- sp->width = vp->width;
- sp->height = vp->height;
- }
- // if (realloc_texture(&is->sub_texture, SDL_PIXELFORMAT_ARGB8888,
- // sp->width, sp->height, SDL_BLENDMODE_BLEND, 1) < 0) return;
- #if 1
- for (unsigned int i = 0; i < sp->sub.num_rects; i++) {
- AVSubtitleRect* sub_rect = sp->sub.rects[i];
- if (sub_rect->type == SUBTITLE_ASS) {
- qDebug("subtitle[%d], format:%d, type:%d, text:%s, flags:%d",
- i,
- sp->sub.format,
- sub_rect->type,
- sub_rect->text,
- sub_rect->flags);
- // QString ass = QString::fromUtf8(sub_rect->ass);
- QString ass = QString::fromLocal8Bit(
- QString::fromStdString(sub_rect->ass).toUtf8());
- QStringList assList = ass.split(",");
- if (assList.size() > 8) {
- ass = assList[8];
- qDebug("ass: %s", qUtf8Printable(ass));
- parse_subtitle_ass(ass);
- }
- } else {
- qWarning("not handled yet, type:%d", sub_rect->type);
- }
- }
- #else
- for (i = 0; i < sp->sub.num_rects; i++) {
- AVSubtitleRect* sub_rect = sp->sub.rects[i];
- sub_rect->x = av_clip(sub_rect->x, 0, sp->width);
- sub_rect->y = av_clip(sub_rect->y, 0, sp->height);
- sub_rect->w = av_clip(sub_rect->w, 0, sp->width - sub_rect->x);
- sub_rect->h = av_clip(sub_rect->h, 0, sp->height - sub_rect->y);
- is->sub_convert_ctx = sws_getCachedContext(is->sub_convert_ctx,
- sub_rect->w,
- sub_rect->h,
- AV_PIX_FMT_PAL8,
- sub_rect->w,
- sub_rect->h,
- AV_PIX_FMT_BGRA,
- 0,
- nullptr,
- nullptr,
- nullptr);
- if (!is->sub_convert_ctx) {
- av_log(nullptr, AV_LOG_FATAL, "Cannot initialize the conversion context\n");
- return;
- }
- #if 1
- sws_scale(is->sub_convert_ctx,
- (const uint8_t* const*) sub_rect->data,
- sub_rect->linesize,
- 0,
- sub_rect->h,
- pixels,
- pitch);
- #else
- if (!SDL_LockTexture(is->sub_texture,
- (SDL_Rect*) sub_rect,
- (void**) pixels,
- pitch)) {
- sws_scale(is->sub_convert_ctx,
- (const uint8_t* const*) sub_rect->data,
- sub_rect->linesize,
- 0,
- sub_rect->h,
- pixels,
- pitch);
- SDL_UnlockTexture(is->sub_texture);
- }
- #endif
- }
- #endif
- sp->uploaded = 1;
- }
- } else {
- sp = nullptr;
- }
- }
- AVFrame* pFrameRGB = pResample->pFrameRGB; // dst
- AVCodecContext* pVideoCtx = is->viddec.avctx;
- AVFrame* pFrame = vp->frame;
- // AVPixelFormat fmt = (AVPixelFormat)pFrame->format; // 0
- // const char* fmt_name = av_get_pix_fmt_name(fmt);
- // AVHWFramesContext* ctx =
- // (AVHWFramesContext*)pVideoCtx->hw_frames_ctx->data; AVPixelFormat sw_fmt =
- // ctx->sw_format;
- // qDebug("frame w:%d,h:%d, pts:%lld, dts:%lld", pVideoCtx->width,
- // pVideoCtx->height, pFrame->pts, pFrame->pkt_dts);
- // TODO: 不转换
- sws_scale(pResample->sws_ctx,
- (uint8_t const* const*) pFrame->data,
- pFrame->linesize,
- 0,
- pVideoCtx->height,
- pFrameRGB->data,
- pFrameRGB->linesize);
- // QImage img(pVideoCtx->width, pVideoCtx->height, QImage::Format_RGB888);
- // for (int y = 0; y < pVideoCtx->height; ++y) {
- // memcpy(img.scanLine(y),
- // pFrameRGB->data[0] + y * pFrameRGB->linesize[0],
- // pVideoCtx->width * 3);
- // }
- // emit frame_ready(img);
- if (m_onFrameReady) {
- m_onFrameReady(pFrameRGB);
- }
- }
- bool VideoPlayThread::init_resample_param(AVCodecContext* pVideo, bool bHardware)
- {
- Video_Resample* pResample = &m_Resample;
- if (pVideo) {
- enum AVPixelFormat pix_fmt = pVideo->pix_fmt; // frame format after decode
- if (bHardware)
- pix_fmt = AV_PIX_FMT_NV12;
- struct SwsContext* sws_ctx
- = sws_getContext(pVideo->width,
- pVideo->height,
- pix_fmt, // AV_PIX_FMT_YUV420P
- pVideo->width,
- pVideo->height,
- AV_PIX_FMT_RGB24, // sws_scale destination color scheme
- SWS_BILINEAR,
- nullptr,
- nullptr,
- nullptr);
- AVFrame* pFrameRGB = av_frame_alloc();
- if (!pFrameRGB) {
- printf("Could not allocate rgb frame.\n");
- return false;
- }
- pFrameRGB->width = pVideo->width;
- pFrameRGB->height = pVideo->height;
- pFrameRGB->format = AV_PIX_FMT_RGB24;
- int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, pVideo->width, pVideo->height, 32);
- uint8_t* const buffer_RGB = (uint8_t*) av_malloc(numBytes * sizeof(uint8_t));
- if (!buffer_RGB) {
- printf("Could not allocate buffer.\n");
- return false;
- }
- av_image_fill_arrays(pFrameRGB->data,
- pFrameRGB->linesize,
- buffer_RGB,
- AV_PIX_FMT_RGB24,
- pVideo->width,
- pVideo->height,
- 32);
- pResample->sws_ctx = sws_ctx;
- pResample->pFrameRGB = pFrameRGB;
- pResample->buffer_RGB = buffer_RGB;
- return true;
- }
- return false;
- }
- void VideoPlayThread::final_resample_param()
- {
- Video_Resample* pResample = &m_Resample;
- // Free video resample context
- sws_freeContext(pResample->sws_ctx);
- // Free the RGB image
- av_free(pResample->buffer_RGB);
- av_frame_free(&pResample->pFrameRGB);
- av_free(pResample->pFrameRGB);
- }
- void VideoPlayThread::parse_subtitle_ass(const QString& text)
- {
- QString str = text;
- str.remove(m_assFilter);
- str.replace(m_assNewLineReplacer, "\n");
- str = str.trimmed();
- // emit subtitle_ready(str);
- if (m_onSubtitleReady)
- m_onSubtitleReady(str);
- }
|