PR #22748 opened by Guido Cella (guidocella) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22748 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22748.patch
This rebases the patches from https://trac.ffmpeg.org/ticket/3233, which add support for audio tracks in PSP PMF videos. Specifically, it combines: https://ffmpeg.org/pipermail/ffmpeg-devel/2014-December/166169.html https://trac.ffmpeg.org/attachment/ticket/3233/bazz_patches.zip https://trac.ffmpeg.org/attachment/ticket/3233/0001-Fix-detecting-ATRAC3-audio-from-MPS-files.patch Changes other than the rebasing are: Because these patches broke individual ATRAC 3 PLUS audio files, add a mode field used to run the logic only if this is a PMF audio stream. Prevent crashing at the end of videos with "Couldn't combine frame: bytes needed=65535, bytes supplied=138" by returning when 0xFFFF is found, because that signifies the end of stream. After seeking, don't pass the leftover bytes from the previous frame to ff_combine_frame to not log a bunch of errors. In atrac3plusdec.c, remove if (!avctx->block_align) av_log(avctx, AV_LOG_ERROR, "block_align is not set\n") because it was logged when starting to play every PMF video. bazz's patch also removed the returned error in this condition because it is necessary to play certain PMF videos fully, though I can't verify this because the video he linked in the issue is now down. Rename some variables and reduced nesting. >From ad56e0a961df51cf3f4f86a231932cc1f650c01c Mon Sep 17 00:00:00 2001 From: Guido Cella <[email protected]> Date: Wed, 8 Apr 2026 10:46:23 +0200 Subject: [PATCH] libavcodec/atrac3plus_parser: support audio in PMF videos This rebases the patches from https://trac.ffmpeg.org/ticket/3233, which add support for audio tracks in PSP PMF videos. Specifically, it combines: https://ffmpeg.org/pipermail/ffmpeg-devel/2014-December/166169.html https://trac.ffmpeg.org/attachment/ticket/3233/bazz_patches.zip https://trac.ffmpeg.org/attachment/ticket/3233/0001-Fix-detecting-ATRAC3-audio-from-MPS-files.patch Changes other than the rebasing are: Because these patches broke individual ATRAC 3 PLUS audio files, add a mode field used to run the logic only if this is a PMF audio stream. Prevent crashing at the end of videos with "Couldn't combine frame: bytes needed=65535, bytes supplied=138" by returning when 0xFFFF is found, because that signifies the end of stream. After seeking, don't pass the leftover bytes from the previous frame to ff_combine_frame to not log a bunch of errors. In atrac3plusdec.c, remove if (!avctx->block_align) av_log(avctx, AV_LOG_ERROR, "block_align is not set\n") because it was logged when starting to play every PMF video. bazz's patch also removed the returned error in this condition because it is necessary to play certain PMF videos fully, though I can't verify this because the video he linked in the issue is now down. Rename some variables and reduced nesting. Co-Authored-by: Maxim Poliakovski <[email protected]> Co-Authored-by: Michael "Bazz" Bazzinotti <[email protected]> Co-Authored-by: Misty De Meo <[email protected]> --- libavcodec/Makefile | 1 + libavcodec/atrac3plus_parser.c | 188 +++++++++++++++++++++++++++++++++ libavcodec/atrac3plusdec.c | 5 - libavcodec/parsers.c | 1 + libavformat/mpeg.c | 15 +++ libavformat/mpeg.h | 1 + 6 files changed, 206 insertions(+), 5 deletions(-) create mode 100644 libavcodec/atrac3plus_parser.c diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 687121e337..33a2f0db96 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1243,6 +1243,7 @@ OBJS-$(CONFIG_ADX_PARSER) += adx_parser.o OBJS-$(CONFIG_AHX_PARSER) += ahx_parser.o OBJS-$(CONFIG_AMR_PARSER) += amr_parser.o OBJS-$(CONFIG_APV_PARSER) += apv_parser.o +OBJS-$(CONFIG_ATRAC3P_PARSER) += atrac3plus_parser.o OBJS-$(CONFIG_AV1_PARSER) += av1_parser.o av1_parse.o OBJS-$(CONFIG_AVS2_PARSER) += avs2.o avs2_parser.o OBJS-$(CONFIG_AVS3_PARSER) += avs3_parser.o diff --git a/libavcodec/atrac3plus_parser.c b/libavcodec/atrac3plus_parser.c new file mode 100644 index 0000000000..0f5a2ba441 --- /dev/null +++ b/libavcodec/atrac3plus_parser.c @@ -0,0 +1,188 @@ +/* + * ATRAC3+ parser + * + * Copyright (C) 2026 Guido Cella + * Copyright (C) 2016 Michael "Bazz" Bazzinotti + * Copyright (C) 2014 Maxim Poliakovski + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavformat/oma.h" +#include "libavutil/channel_layout.h" +#include "get_bits.h" +#include "parser.h" +#include "parser_internal.h" + +typedef struct Atrac3PlusParseContext { + ParseContext pc; + int mode; + uint8_t hdr[8]; + int hdr_bytes_needed; + int sample_rate, channel_id, frame_size; + uint8_t past_header; +} Atrac3PlusParseContext; + +static int parse_sound_frame_header(Atrac3PlusParseContext *ctx, + const uint8_t *buf) +{ + uint16_t atrac_config; + + if (AV_RB16(buf) != 0x0FD0) + return AVERROR_INVALIDDATA; + + atrac_config = AV_RB16(&buf[2]); + ctx->sample_rate = ff_oma_srate_tab[(atrac_config >> 13) & 7] * 100; + ctx->channel_id = (atrac_config >> 10) & 7; + ctx->frame_size = ((atrac_config & 0x3FF) * 8) + 8; + + if (!ctx->channel_id || !ctx->sample_rate || !ctx->frame_size) + return AVERROR_INVALIDDATA; + + return 0; +} + +static int atrac3p_parse(AVCodecParserContext *s, + AVCodecContext *avctx, + const uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size) +{ + Atrac3PlusParseContext *ctx = s->priv_data; + const uint8_t *hdr_buf = buf; + int frame_size; + int hdr_bytes = 8; + int next = 0; + uint16_t remaining_bytes = AV_RB16(buf); + + // Return early with standalone ATRAC3+ files or they would + // stop working. PMF audio packets start with a 2-byte count of + // bytes of the previous frame at the start of this packet. After the + // bytes from the previous frame is the sync word 0x0FD0. + // Standalone ATRAC3+ frames have no header, and even if they could + // theoretically contain 0x0FD0 after sync_offset, + // remaining_bytes/sync_offset is much bigger than buf_size in practice, so + // there is no risk of misdetection. + if (!ctx->mode) { + int sync_offset = 2 + remaining_bytes; + + if (sync_offset + 2 <= buf_size && AV_RB16(buf + sync_offset) == 0x0FD0) { + ctx->mode = 1; // PMF + } else { + ctx->mode = 2; // standalone ATRAC3+ + } + } + + if (ctx->mode == 2 || s->flags & PARSER_FLAG_COMPLETE_FRAMES) { + *poutbuf = buf; + *poutbuf_size = buf_size; + return buf_size; + } + + if (buf_size >= 2 && remaining_bytes != 0xFD0) { + next += 2; + buf += 2; + buf_size -= 2; + hdr_buf = buf; + + if (ctx->hdr_bytes_needed) { + if (buf_size >= ctx->hdr_bytes_needed) { + memcpy(&ctx->hdr[8 - ctx->hdr_bytes_needed], + buf, ctx->hdr_bytes_needed); + hdr_bytes = ctx->hdr_bytes_needed; + ctx->hdr_bytes_needed = 0; + hdr_buf = ctx->hdr; + } + } else if (remaining_bytes == 0xFFFF) { + // end of stream + *poutbuf = NULL; + *poutbuf_size = 0; + return buf_size + next; + } else if (ctx->past_header) { + ctx->past_header = 0; + goto process; + } else if (buf_size < remaining_bytes) { + av_log(avctx, AV_LOG_ERROR, + "Couldn't combine frame: bytes needed=%hu, bytes supplied=%d\n", + remaining_bytes, buf_size); + return AVERROR_INVALIDDATA; + } else if (ctx->pc.index) { + next += remaining_bytes; + ff_combine_frame(&ctx->pc, remaining_bytes, &buf, &buf_size); + + *poutbuf = buf; + *poutbuf_size = buf_size; + return next; + } else { + // After a seek, skip remaining_bytes because we don't have the + // start of the frame. + next += remaining_bytes; + buf += remaining_bytes; + buf_size -= remaining_bytes; + hdr_buf = buf; + } + } + + if (buf_size < hdr_bytes) { + // We got an incomplete header. + memcpy(ctx->hdr, buf, buf_size); + ctx->hdr_bytes_needed = 8 - buf_size; + *poutbuf = NULL; + *poutbuf_size = 0; + return buf_size; + } + + if (parse_sound_frame_header(ctx, hdr_buf)) { + av_log(avctx, AV_LOG_ERROR, "Invalid sound frame header!\n"); + return AVERROR_INVALIDDATA; + } + + avctx->sample_rate = ctx->sample_rate; + avctx->block_align = ctx->frame_size; + avctx->bit_rate = ctx->sample_rate * ctx->frame_size * 8 / 2048; + + av_channel_layout_default(&avctx->ch_layout, ctx->channel_id); + + next += hdr_bytes; + buf += hdr_bytes; + buf_size -= hdr_bytes; + if (!buf_size) + ctx->past_header = 1; +process: + frame_size = ctx->frame_size; + + if (buf_size < frame_size) + frame_size = END_NOT_FOUND; + + if (ff_combine_frame(&ctx->pc, frame_size, &buf, &buf_size) < 0) { + *poutbuf = NULL; + *poutbuf_size = 0; + return buf_size + next; + } + + next += frame_size >= 0 ? frame_size : buf_size; + + *poutbuf = buf; + *poutbuf_size = buf_size; + return next; +} + +const FFCodecParser ff_atrac3p_parser = { + PARSER_CODEC_LIST(AV_CODEC_ID_ATRAC3P), + .priv_data_size = sizeof(Atrac3PlusParseContext), + .parse = atrac3p_parse, + .close = ff_parse_close, +}; diff --git a/libavcodec/atrac3plusdec.c b/libavcodec/atrac3plusdec.c index 9696a523be..de153ee233 100644 --- a/libavcodec/atrac3plusdec.c +++ b/libavcodec/atrac3plusdec.c @@ -176,11 +176,6 @@ static av_cold int atrac3p_decode_init(AVCodecContext *avctx) float scale; int i, ch, ret; - if (!avctx->block_align) { - av_log(avctx, AV_LOG_ERROR, "block_align is not set\n"); - return AVERROR(EINVAL); - } - /* initialize IPQF */ scale = 32.0 / 32768.0; ret = av_tx_init(&ctx->ipqf_dct_ctx, &ctx->ipqf_dct_fn, AV_TX_FLOAT_MDCT, diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c index 162b96cb69..1e682d2ab3 100644 --- a/libavcodec/parsers.c +++ b/libavcodec/parsers.c @@ -44,6 +44,7 @@ extern const FFCodecParser ff_adx_parser; extern const FFCodecParser ff_ahx_parser; extern const FFCodecParser ff_amr_parser; extern const FFCodecParser ff_apv_parser; +extern const FFCodecParser ff_atrac3p_parser; extern const FFCodecParser ff_av1_parser; extern const FFCodecParser ff_avs2_parser; extern const FFCodecParser ff_avs3_parser; diff --git a/libavformat/mpeg.c b/libavformat/mpeg.c index a7a2ef78e6..a645619bf1 100644 --- a/libavformat/mpeg.c +++ b/libavformat/mpeg.c @@ -575,6 +575,21 @@ redo: else request_probe= 1; type = AVMEDIA_TYPE_VIDEO; + // Sony PSP video with ATRAC-3 audio + } else if (!(startcode & STREAM_TYPE_AUDIO_ATRAC3)) { + avio_r8(s->pb); // skip padding + len--; + for (i = 0; i < s->nb_streams; i++) { + st = s->streams[i]; + if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && + st->codecpar->codec_id == AV_CODEC_ID_ATRAC3P && + st->id - 0x1BD0 == (startcode & 0xF)) + goto found; + } + + startcode = 0x1BD0 + (startcode & 0xF); + type = AVMEDIA_TYPE_AUDIO; + codec_id = AV_CODEC_ID_ATRAC3P; } else if (startcode == PRIVATE_STREAM_2) { type = AVMEDIA_TYPE_DATA; codec_id = AV_CODEC_ID_DVD_NAV; diff --git a/libavformat/mpeg.h b/libavformat/mpeg.h index 20592eb184..0cbb3ac5b4 100644 --- a/libavformat/mpeg.h +++ b/libavformat/mpeg.h @@ -60,6 +60,7 @@ #define STREAM_TYPE_VIDEO_CAVS 0x42 #define STREAM_TYPE_AUDIO_AC3 0x81 +#define STREAM_TYPE_AUDIO_ATRAC3 0xF0 static const int lpcm_freq_tab[4] = { 48000, 96000, 44100, 32000 }; -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
