$OpenBSD: patch-Core_HW_MediaEngine_cpp,v 1.2 2021/09/19 00:34:55 bentley Exp $

Fix build with ffmpeg-4.4 from upstream

Index: Core/HW/MediaEngine.cpp
--- Core/HW/MediaEngine.cpp.orig
+++ Core/HW/MediaEngine.cpp
@@ -314,8 +314,10 @@ bool MediaEngine::openContext(bool keepReadPos) {
 	av_dict_free(&open_opt);
 
 	if (!SetupStreams()) {
-		// Fallback to old behavior.
-		if (avformat_find_stream_info(m_pFormatCtx, NULL) < 0) {
+		// Fallback to old behavior.  Reads too much and corrupts when game doesn't read fast enough.
+		// SetupStreams sometimes work for newer FFmpeg 3.1+ now, but sometimes framerate is missing.
+		WARN_LOG_REPORT_ONCE(setupStreams, ME, "Failed to read valid video stream data from header");
+		if (avformat_find_stream_info(m_pFormatCtx, nullptr) < 0) {
 			closeContext();
 			return false;
 		}
@@ -328,8 +330,14 @@ bool MediaEngine::openContext(bool keepReadPos) {
 
 	if (m_videoStream == -1) {
 		// Find the first video stream
-		for(int i = 0; i < (int)m_pFormatCtx->nb_streams; i++) {
-			if(m_pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+		for (int i = 0; i < (int)m_pFormatCtx->nb_streams; i++) {
+			const AVStream *s = m_pFormatCtx->streams[i];
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 33, 100)
+			AVMediaType type = s->codecpar->codec_type;
+#else
+			AVMediaType type = s->codec->codec_type;
+#endif
+			if (type == AVMEDIA_TYPE_VIDEO) {
 				m_videoStream = i;
 				break;
 			}
@@ -361,8 +369,13 @@ void MediaEngine::closeContext()
 		av_free(m_pIOContext->buffer);
 	if (m_pIOContext)
 		av_free(m_pIOContext);
-	for (auto it = m_pCodecCtxs.begin(), end = m_pCodecCtxs.end(); it != end; ++it)
-		avcodec_close(it->second);
+	for (auto it : m_pCodecCtxs) {
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 33, 100)
+		avcodec_free_context(&it.second);
+#else
+		avcodec_close(it.second);
+#endif
+	}
 	m_pCodecCtxs.clear();
 	if (m_pFormatCtx)
 		avformat_close_input(&m_pFormatCtx);
@@ -411,7 +424,12 @@ bool MediaEngine::addVideoStream(int streamNum, int st
 				streamId = PSMF_VIDEO_STREAM_ID | streamNum;
 
 			stream->id = 0x00000100 | streamId;
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 33, 100)
+			stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+			stream->codecpar->codec_id = AV_CODEC_ID_H264;
+#else
 			stream->request_probe = 0;
+#endif
 			stream->need_parsing = AVSTREAM_PARSE_FULL;
 			// We could set the width here, but we don't need to.
 			if (streamNum >= m_expectedVideoStreams) {
@@ -496,22 +514,31 @@ bool MediaEngine::setVideoStream(int streamNum, bool f
 		if ((u32)streamNum >= m_pFormatCtx->nb_streams) {
 			return false;
 		}
-		AVCodecContext *m_pCodecCtx = m_pFormatCtx->streams[streamNum]->codec;
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57,33,100)
-		AVCodecParameters *m_pCodecPar = m_pFormatCtx->streams[streamNum]->codecpar;
 
-		// Update from deprecated public codec context
-		if (avcodec_parameters_from_context(m_pCodecPar, m_pCodecCtx) < 0) {
+		AVStream *stream = m_pFormatCtx->streams[streamNum];
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 33, 100)
+		AVCodec *pCodec = avcodec_find_decoder(stream->codecpar->codec_id);
+		if (!pCodec) {
+			WARN_LOG_REPORT(ME, "Could not find decoder for %d", (int)stream->codecpar->codec_id);
 			return false;
 		}
-#endif
-
+		AVCodecContext *m_pCodecCtx = avcodec_alloc_context3(pCodec);
+		int paramResult = avcodec_parameters_to_context(m_pCodecCtx, stream->codecpar);
+		if (paramResult < 0) {
+			WARN_LOG_REPORT(ME, "Failed to prepare context parameters: %08x", paramResult);
+			return false;
+		}
+#else
+		AVCodecContext *m_pCodecCtx = stream->codec;
 		// Find the decoder for the video stream
 		AVCodec *pCodec = avcodec_find_decoder(m_pCodecCtx->codec_id);
 		if (pCodec == nullptr) {
 			return false;
 		}
+#endif
 
+		m_pCodecCtx->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT | AV_CODEC_FLAG_LOW_DELAY;
+
 		AVDictionary *opt = nullptr;
 		// Allow ffmpeg to use any number of threads it wants.  Without this, it doesn't use threads.
 		av_dict_set(&opt, "threads", "0", 0);
@@ -651,7 +678,22 @@ bool MediaEngine::stepVideo(int videoPixelMode, bool s
 				av_free_packet(&packet);
 #endif
 
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
+			if (packet.size != 0)
+				avcodec_send_packet(m_pCodecCtx, &packet);
+			int result = avcodec_receive_frame(m_pCodecCtx, m_pFrame);
+			if (result == 0) {
+				result = m_pFrame->pkt_size;
+				frameFinished = 1;
+			} else if (result == AVERROR(EAGAIN)) {
+				result = 0;
+				frameFinished = 0;
+			} else {
+				frameFinished = 0;
+			}
+#else
 			int result = avcodec_decode_video2(m_pCodecCtx, m_pFrame, &frameFinished, &packet);
+#endif
 			if (frameFinished) {
 				if (!m_pFrameRGB) {
 					setVideoDim();
@@ -666,10 +708,28 @@ bool MediaEngine::stepVideo(int videoPixelMode, bool s
 						m_pCodecCtx->height, m_pFrameRGB->data, m_pFrameRGB->linesize);
 				}
 
-				if (av_frame_get_best_effort_timestamp(m_pFrame) != AV_NOPTS_VALUE)
-					m_videopts = av_frame_get_best_effort_timestamp(m_pFrame) + av_frame_get_pkt_duration(m_pFrame) - m_firstTimeStamp;
-				else
-					m_videopts += av_frame_get_pkt_duration(m_pFrame);
+#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 58, 100)
+				int64_t bestPts = m_pFrame->best_effort_timestamp;
+				int64_t ptsDuration = m_pFrame->pkt_duration;
+#else
+				int64_t bestPts = av_frame_get_best_effort_timestamp(m_pFrame);
+				int64_t ptsDuration = av_frame_get_pkt_duration(m_pFrame);
+#endif
+				if (ptsDuration == 0) {
+					if (m_lastPts == bestPts - m_firstTimeStamp || bestPts == AV_NOPTS_VALUE) {
+						// TODO: Assuming 29.97 if missing.
+						m_videopts += 3003;
+					} else {
+						m_videopts = bestPts - m_firstTimeStamp;
+						m_lastPts = m_videopts;
+					}
+				} else if (bestPts != AV_NOPTS_VALUE) {
+					m_videopts = bestPts + ptsDuration - m_firstTimeStamp;
+					m_lastPts = m_videopts;
+				} else {
+					m_videopts += ptsDuration;
+					m_lastPts = m_videopts;
+				}
 				bGetFrame = true;
 			}
 			if (result <= 0 && dataEnd) {
