Skip to content

Commit 558b462

Browse files
Mpeg: Parse video streams from PSMF header.
Without doing this, FFmpeg will try to probe the streams to detect them instead. When it does this, sometimes it tries to read beyond the data that's available - and then gets confused by EOFs. Parsing this way allows us to control the situation. An example is Valkyrie Profile, corruption in the first frames of the second video during the intro. Thi doesn't fix it yet, but now it's just a matter of buffering.
1 parent dcc2541 commit 558b462

File tree

5 files changed

+86
-34
lines changed

5 files changed

+86
-34
lines changed

Core/HLE/sceMpeg.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,6 @@ static void AnalyzeMpeg(u8 *buffer, MpegContext *ctx) {
337337
// TODO: Does this make any sense?
338338
ctx->mediaengine->loadStream(buffer, ctx->mpegOffset, 0);
339339
}
340-
ctx->mediaengine->setVideoDim();
341340
}
342341

343342
// When used with scePsmf, some applications attempt to use sceMpegQueryStreamOffset

Core/HLE/sceMpeg.h

+3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ static const int PSMF_STREAM_SIZE_OFFSET = 0xC;
4242
static const int PSMF_FIRST_TIMESTAMP_OFFSET = 0x54;
4343
static const int PSMF_LAST_TIMESTAMP_OFFSET = 0x5A;
4444

45+
static const int PSMF_VIDEO_STREAM_ID = 0xE0;
46+
static const int PSMF_AUDIO_STREAM_ID = 0xBD;
47+
4548
struct SceMpegAu {
4649
s64_le pts; // presentation time stamp
4750
s64_le dts; // decode time stamp

Core/HLE/scePsmf.cpp

-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@
3333
#include <algorithm>
3434

3535
// "Go Sudoku" is a good way to test this code...
36-
const int PSMF_VIDEO_STREAM_ID = 0xE0;
37-
const int PSMF_AUDIO_STREAM_ID = 0xBD;
3836
const int PSMF_AVC_STREAM = 0;
3937
const int PSMF_ATRAC_STREAM = 1;
4038
const int PSMF_PCM_STREAM = 2;

Core/HW/MediaEngine.cpp

+80-29
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ void MediaEngine::closeMedia() {
168168
}
169169

170170
void MediaEngine::DoState(PointerWrap &p) {
171-
auto s = p.Section("MediaEngine", 1, 4);
171+
auto s = p.Section("MediaEngine", 1, 5);
172172
if (!s)
173173
return;
174174

@@ -181,6 +181,11 @@ void MediaEngine::DoState(PointerWrap &p) {
181181
} else {
182182
m_mpegheaderSize = sizeof(m_mpegheader);
183183
}
184+
if (s >= 5) {
185+
p.Do(m_mpegheaderReadPos);
186+
} else {
187+
m_mpegheaderReadPos = m_mpegheaderSize;
188+
}
184189

185190
p.Do(m_ringbuffersize);
186191

@@ -194,8 +199,6 @@ void MediaEngine::DoState(PointerWrap &p) {
194199
u32 hasopencontext = false;
195200
#endif
196201
p.Do(hasopencontext);
197-
if (hasopencontext && p.mode == p.MODE_READ)
198-
openContext();
199202
if (m_pdata)
200203
m_pdata->DoState(p);
201204
if (m_demux)
@@ -209,6 +212,10 @@ void MediaEngine::DoState(PointerWrap &p) {
209212
p.Do(m_lastTimeStamp);
210213
}
211214

215+
if (hasopencontext && p.mode == p.MODE_READ) {
216+
openContext(true);
217+
}
218+
212219
p.Do(m_isVideoEnd);
213220
bool noAudioDataRemoved;
214221
p.Do(noAudioDataRemoved);
@@ -219,17 +226,14 @@ void MediaEngine::DoState(PointerWrap &p) {
219226
}
220227
}
221228

222-
int _MpegReadbuffer(void *opaque, uint8_t *buf, int buf_size)
223-
{
229+
static int MpegReadbuffer(void *opaque, uint8_t *buf, int buf_size) {
224230
MediaEngine *mpeg = (MediaEngine *)opaque;
225231

226232
int size = buf_size;
227233
if (mpeg->m_mpegheaderReadPos < mpeg->m_mpegheaderSize) {
228234
size = std::min(buf_size, mpeg->m_mpegheaderSize - mpeg->m_mpegheaderReadPos);
229235
memcpy(buf, mpeg->m_mpegheader + mpeg->m_mpegheaderReadPos, size);
230236
mpeg->m_mpegheaderReadPos += size;
231-
} else if (mpeg->m_mpegheaderReadPos == mpeg->m_mpegheaderSize) {
232-
return 0;
233237
} else {
234238
size = mpeg->m_pdata->pop_front(buf, buf_size);
235239
if (size > 0)
@@ -238,33 +242,73 @@ int _MpegReadbuffer(void *opaque, uint8_t *buf, int buf_size)
238242
return size;
239243
}
240244

241-
bool MediaEngine::openContext() {
245+
bool MediaEngine::SetupStreams() {
246+
#ifdef USE_FFMPEG
247+
const u32 magic = *(u32_le *)&m_mpegheader[0];
248+
if (magic != PSMF_MAGIC) {
249+
WARN_LOG_REPORT(ME, "Could not setup streams, bad magic: %08x", magic);
250+
return false;
251+
}
252+
int numStreams = *(u16_be *)&m_mpegheader[0x80];
253+
if (numStreams <= 0 || numStreams > 8) {
254+
// Looks crazy. Let's bail out and let FFmpeg handle it.
255+
WARN_LOG_REPORT(ME, "Could not setup streams, unexpected stream count: %d", numStreams);
256+
return false;
257+
}
258+
259+
// Looking good. Let's add those streams.
260+
const AVCodec *h264_codec = avcodec_find_decoder(AV_CODEC_ID_H264);
261+
for (int i = 0; i < numStreams; i++) {
262+
const u8 *const currentStreamAddr = m_mpegheader + 0x82 + i * 16;
263+
int streamId = currentStreamAddr[0];
264+
265+
// We only set video streams. We demux the audio stream separately.
266+
if ((streamId & PSMF_VIDEO_STREAM_ID) == PSMF_VIDEO_STREAM_ID) {
267+
AVStream *stream = avformat_new_stream(m_pFormatCtx, h264_codec);
268+
stream->id = 0x00000100 | streamId;
269+
stream->request_probe = 0;
270+
stream->need_parsing = AVSTREAM_PARSE_FULL;
271+
// We could set the width here, but we don't need to.
272+
}
273+
}
274+
275+
#endif
276+
return true;
277+
}
278+
279+
bool MediaEngine::openContext(bool keepReadPos) {
242280
#ifdef USE_FFMPEG
243281
InitFFmpeg();
244282

245283
if (m_pFormatCtx || !m_pdata)
246284
return false;
247-
m_mpegheaderReadPos = 0;
285+
if (!keepReadPos) {
286+
m_mpegheaderReadPos = 0;
287+
}
248288
m_decodingsize = 0;
249289

250-
u8* tempbuf = (u8*)av_malloc(m_bufSize);
290+
m_bufSize = std::max(m_bufSize, m_mpegheaderSize);
291+
u8 *tempbuf = (u8*)av_malloc(m_bufSize);
251292

252293
m_pFormatCtx = avformat_alloc_context();
253-
m_pIOContext = avio_alloc_context(tempbuf, m_bufSize, 0, (void*)this, _MpegReadbuffer, NULL, 0);
294+
m_pIOContext = avio_alloc_context(tempbuf, m_bufSize, 0, (void*)this, &MpegReadbuffer, nullptr, nullptr);
254295
m_pFormatCtx->pb = m_pIOContext;
255296

256297
// Open video file
257298
AVDictionary *open_opt = nullptr;
258299
av_dict_set_int(&open_opt, "probesize", m_mpegheaderSize, 0);
259-
if (avformat_open_input((AVFormatContext**)&m_pFormatCtx, NULL, NULL, &open_opt) != 0) {
300+
if (avformat_open_input((AVFormatContext**)&m_pFormatCtx, nullptr, nullptr, &open_opt) != 0) {
260301
av_dict_free(&open_opt);
261302
return false;
262303
}
263304
av_dict_free(&open_opt);
264305

265-
if (avformat_find_stream_info(m_pFormatCtx, NULL) < 0) {
266-
closeContext();
267-
return false;
306+
if (!SetupStreams()) {
307+
// Fallback to old behavior.
308+
if (avformat_find_stream_info(m_pFormatCtx, NULL) < 0) {
309+
closeContext();
310+
return false;
311+
}
268312
}
269313

270314
if (m_videoStream >= (int)m_pFormatCtx->nb_streams) {
@@ -290,8 +334,6 @@ bool MediaEngine::openContext() {
290334
setVideoDim();
291335
m_audioContext = new SimpleAudio(m_audioType, 44100, 2);
292336
m_isVideoEnd = false;
293-
m_mpegheaderReadPos++;
294-
av_seek_frame(m_pFormatCtx, m_videoStream, 0, 0);
295337
#endif // USE_FFMPEG
296338
return true;
297339
}
@@ -354,8 +396,7 @@ int MediaEngine::addStreamData(const u8 *buffer, int addSize) {
354396
#ifdef USE_FFMPEG
355397
if (!m_pFormatCtx && m_pdata->getQueueSize() >= 2048) {
356398
m_mpegheaderSize = m_pdata->get_front(m_mpegheader, sizeof(m_mpegheader));
357-
int mpegoffset = (int)(*(s32_be*)(m_mpegheader + 8));
358-
m_pdata->pop_front(0, mpegoffset);
399+
m_pdata->pop_front(0, m_mpegheaderSize);
359400
openContext();
360401
}
361402
#endif // USE_FFMPEG
@@ -418,8 +459,7 @@ bool MediaEngine::setVideoStream(int streamNum, bool force) {
418459
}
419460

420461
// Open codec
421-
AVDictionary *optionsDict = 0;
422-
if (avcodec_open2(m_pCodecCtx, pCodec, &optionsDict) < 0) {
462+
if (avcodec_open2(m_pCodecCtx, pCodec, nullptr) < 0) {
423463
return false; // Could not open codec
424464
}
425465
m_pCodecCtxs[streamNum] = m_pCodecCtx;
@@ -451,11 +491,19 @@ bool MediaEngine::setVideoDim(int width, int height)
451491
}
452492

453493
// Allocate video frame
454-
m_pFrame = av_frame_alloc();
494+
if (!m_pFrame) {
495+
m_pFrame = av_frame_alloc();
496+
}
455497

456498
sws_freeContext(m_sws_ctx);
457499
m_sws_ctx = NULL;
458500
m_sws_fmt = -1;
501+
502+
if (m_desWidth == 0 || m_desHeight == 0) {
503+
// Can't setup SWS yet, so stop for now.
504+
return false;
505+
}
506+
459507
updateSwsFormat(GE_CMODE_32BIT_ABGR8888);
460508

461509
// Allocate video frame for RGB24
@@ -523,14 +571,9 @@ bool MediaEngine::stepVideo(int videoPixelMode, bool skipFrame) {
523571
return false;
524572
if (!m_pCodecCtx)
525573
return false;
526-
if ((!m_pFrame)||(!m_pFrameRGB))
574+
if (!m_pFrame)
527575
return false;
528576

529-
updateSwsFormat(videoPixelMode);
530-
// TODO: Technically we could set this to frameWidth instead of m_desWidth for better perf.
531-
// Update the linesize for the new format too. We started with the largest size, so it should fit.
532-
m_pFrameRGB->linesize[0] = getPixelFormatBytes(videoPixelMode) * m_desWidth;
533-
534577
AVPacket packet;
535578
av_init_packet(&packet);
536579
int frameFinished;
@@ -551,7 +594,15 @@ bool MediaEngine::stepVideo(int videoPixelMode, bool skipFrame) {
551594

552595
int result = avcodec_decode_video2(m_pCodecCtx, m_pFrame, &frameFinished, &packet);
553596
if (frameFinished) {
554-
if (!skipFrame) {
597+
if (!m_pFrameRGB) {
598+
setVideoDim();
599+
}
600+
if (m_pFrameRGB && !skipFrame) {
601+
updateSwsFormat(videoPixelMode);
602+
// TODO: Technically we could set this to frameWidth instead of m_desWidth for better perf.
603+
// Update the linesize for the new format too. We started with the largest size, so it should fit.
604+
m_pFrameRGB->linesize[0] = getPixelFormatBytes(videoPixelMode) * m_desWidth;
605+
555606
sws_scale(m_sws_ctx, m_pFrame->data, m_pFrame->linesize, 0,
556607
m_pCodecCtx->height, m_pFrameRGB->data, m_pFrameRGB->linesize);
557608
}

Core/HW/MediaEngine.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class MediaEngine
6060
bool loadStream(const u8 *buffer, int readSize, int RingbufferSize);
6161
bool reloadStream();
6262
// open the mpeg context
63-
bool openContext();
63+
bool openContext(bool keepReadPos = false);
6464
void closeContext();
6565

6666
// Returns number of packets actually added. I guess the buffer might be full.
@@ -81,7 +81,6 @@ class MediaEngine
8181
int xpos, int ypos, int width, int height);
8282
int getAudioSamples(u32 bufferPtr);
8383

84-
bool setVideoDim(int width = 0, int height = 0);
8584
s64 getVideoTimeStamp();
8685
s64 getAudioTimeStamp();
8786
s64 getLastTimeStamp();
@@ -94,6 +93,8 @@ class MediaEngine
9493
void DoState(PointerWrap &p);
9594

9695
private:
96+
bool SetupStreams();
97+
bool setVideoDim(int width = 0, int height = 0);
9798
void updateSwsFormat(int videoPixelMode);
9899
int getNextAudioFrame(u8 **buf, int *headerCode1, int *headerCode2);
99100

0 commit comments

Comments
 (0)