Files
ppsspp/Core/HW/AvcDecoder.h
T
Henrik RydgårdandClaude Opus 5 8a343bec57 AvcDecoder: let ffmpeg say when a frame came out damaged
Nothing on this path ever called InitFFmpeg, so ffmpeg's log callback was never
installed and everything it had to say about the bitstream went nowhere. Our own
return codes only report that a frame arrived, not what it was built from, so a
video could fall apart on screen while the log showed two warnings in two
thousand frames.

Also report decode_error_flags and AV_FRAME_FLAG_CORRUPT per frame, which covers
the other half: a frame that decodes "successfully" out of concealment. Coalesced
into one line per run of damage, since a missing reference makes every frame
damaged until the next keyframe.

Not setting AV_EF_EXPLODE, which would make the decoder fail where it currently
conceals - that is a behaviour change, and this is meant to be a reporting one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 16:39:35 -06:00

77 lines
2.7 KiB
C++

// Copyright (c) 2026- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include <vector>
#include "Common/CommonTypes.h"
struct AVCodecContext;
struct AVFrame;
// A plain "hand it one access unit, get a frame back" H.264 decoder.
//
// This exists for sceVideocodec, which is a low level interface to the PSP Media Engine.
// PPSSPP's MediaEngine can't serve that - it is built around sceMpeg's own model, with the
// PSMF demuxer, the ringbuffer and its own frame pacing wrapped around the decoder, so this
// is a second path in addition to that. Implementing the old path around this might happen later.
//
// Output is whatever pixel format the decoder produced, normally YUV420P; the caller converts.
class AvcDecoder {
public:
AvcDecoder();
~AvcDecoder();
// Not copyable.
AvcDecoder(const AvcDecoder &) = delete;
AvcDecoder &operator=(const AvcDecoder &) = delete;
// Decodes one access unit. Returns true when a frame came out - H.264 reorders, so a frame
// is not produced for every unit fed in, and that is not an error.
bool Decode(const u8 *data, int size);
// Throws away any decoder state, for a seek or a new stream.
void Flush();
bool HasFrame() const { return haveFrame_; }
int Width() const { return width_; }
int Height() const { return height_; }
// Only meaningful while HasFrame(). Planes are Y, Cb, Cr; strides are in bytes and are not
// necessarily the width.
const u8 *Plane(int index) const;
int Stride(int index) const;
// True if we were built without ffmpeg, in which case nothing decodes.
static bool IsAvailable();
private:
AVCodecContext *codecCtx_ = nullptr;
AVFrame *frame_ = nullptr;
// The access unit, copied so we can pad it - ffmpeg reads a little past the end of a packet.
// Kept between calls so the usual case doesn't allocate.
std::vector<u8> packetBuf_;
bool haveFrame_ = false;
// For reporting runs of concealed frames rather than one line each - see Decode.
bool wasConcealing_ = false;
int concealedFrames_ = 0;
int frameCount_ = 0;
int width_ = 0;
int height_ = 0;
};