// ***********************************************************/ // video_decode_thread.cpp // // Copy Right @ Steven Huang. All rights reserved. // // Video decode thread. This section includes queues // and dxva2 hardware transmit decoded frame. // ***********************************************************/ #include "video_decode_thread.h" #include Q_LOGGING_CATEGORY(playerControllerVideoDecodeThread, "player.controller.VideoDecodeThread") VideoDecodeThread::VideoDecodeThread(VideoState* pState) : m_pState(pState) {} VideoDecodeThread::~VideoDecodeThread() {} void VideoDecodeThread::stop() { *m_exit = true; m_cv.notify_all(); } 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; double pts; double duration; int ret; AVRational tb = is->video_st->time_base; AVRational frame_rate = av_guess_frame_rate(is->ic, is->video_st, nullptr); if (!frame) { qCWarning(playerControllerVideoDecodeThread) << "[VideoDecodeThread] av_frame_alloc failed!"; return; } 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; if (isExit()) { qCDebug(playerControllerVideoDecodeThread) << "[VideoDecodeThread] m_exit set, exit."; break; } if (is->abort_request) { qCDebug(playerControllerVideoDecodeThread) << "[VideoDecodeThread] abort_request set, exit."; break; } 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; if (ret < 0) { 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; continue; } 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 || last_serial != is->viddec.pkt_serial || last_vfilter_idx != is->vfilter_idx || is->req_vfilter_reconfigure) { av_log(nullptr, AV_LOG_DEBUG, "Video frame changed from size:%dx%d format:%s serial:%d to " "size:%dx%d format:%s serial:%d\n", last_w, last_h, (const char*) av_x_if_null(av_get_pix_fmt_name(last_format), "none"), last_serial, frame->width, frame->height, (const char*) av_x_if_null(av_get_pix_fmt_name((AVPixelFormat) frame->format), "none"), is->viddec.pkt_serial); avfilter_graph_free(&is->vgraph); is->vgraph = avfilter_graph_alloc(); if (!is->vgraph) { ret = AVERROR(ENOMEM); goto the_end; } is->vgraph->nb_threads = 0; if ((ret = configure_video_filters(is->vgraph, is, is->vfilters, frame)) < 0) { goto the_end; } filt_in = is->in_video_filter; filt_out = is->out_video_filter; last_w = frame->width; last_h = frame->height; last_format = (AVPixelFormat) frame->format; last_serial = is->viddec.pkt_serial; last_vfilter_idx = is->vfilter_idx; frame_rate = av_buffersink_get_frame_rate(filt_out); is->req_vfilter_reconfigure = 0; } ret = av_buffersrc_add_frame(filt_in, frame); if (ret < 0) goto the_end; while (ret >= 0) { is->frame_last_returned_time = av_gettime_relative() / 1000000.0; ret = av_buffersink_get_frame_flags(filt_out, frame, 0); if (ret < 0) { if (ret == AVERROR_EOF) is->viddec.finished = is->viddec.pkt_serial; ret = 0; break; } is->frame_last_filter_delay = av_gettime_relative() / 1000000.0 - is->frame_last_returned_time; if (fabs(is->frame_last_filter_delay) > AV_NOSYNC_THRESHOLD / 10.0) is->frame_last_filter_delay = 0; tb = av_buffersink_get_time_base(filt_out); #endif #if 0 duration = (frame_rate.num && frame_rate.den ? av_q2d(AVRational{frame_rate.den, frame_rate.num}) : 0); pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb); ret = queue_picture(is, frame, pts, duration, frame->pkt_pos, is->viddec.pkt_serial); av_frame_unref(frame); #else tmp_frame = frame; if (frame->format == AV_PIX_FMT_DXVA2_VLD) // DXVA2 hardware decode frame { ret = av_hwframe_transfer_data(sw_frame, frame, 0); if (ret < 0) { av_log(nullptr, AV_LOG_WARNING, "Error transferring the data to system memory\n"); goto the_end; } sw_frame->pts = frame->pts; sw_frame->pkt_dts = frame->pkt_dts; tmp_frame = sw_frame; } duration = (frame_rate.num && frame_rate.den ? av_q2d({frame_rate.den, frame_rate.num}) : 0); pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb); ret = queue_picture(is, tmp_frame, pts, duration, frame->pkt_pos, is->viddec.pkt_serial); av_frame_unref(tmp_frame); #endif #if USE_AVFILTER_VIDEO if (is->videoq.serial != is->viddec.pkt_serial) break; } #endif if (ret < 0) goto the_end; } the_end: #if USE_AVFILTER_VIDEO avfilter_graph_free(&is->vgraph); if (is->vfilters) { av_free(is->vfilters); is->vfilters = nullptr; } #endif 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); return; }