diff mbox

[FFmpeg-devel,1/1] mpeg: add experimental support for PSMF audio.

Message ID 20180101093222.74544-2-misty@brew.sh
State Superseded
Headers show

Commit Message

misty@brew.sh Jan. 1, 2018, 9:32 a.m. UTC
From: Maxim Poliakovski <maximumspatium@googlemail.com>

Changes by Misty De Meo <mistydemeo@gmail.com>:

atrac3plus_parser: remove return statements for invalid data

atrac3plus_parser: use libavcodec's oma

atrac3plus_parser: pass along unexpected data unaltered

atrac3plus_parser: adjust bytes_remain type

Change by Michael "Bazz" Bazzinotti <mbazzinotti@gmail.com>:

atrac3plus_parser: don't always fail video for "2nd frame portion found"

Signed-off-by: Misty De Meo <mistydemeo@gmail.com>

wip parser
---
 libavcodec/Makefile            |   1 +
 libavcodec/allcodecs.c         |   1 +
 libavcodec/atrac3plus_parser.c | 170 +++++++++++++++++++++++++++++++++++++++++
 libavformat/mpeg.c             |  27 ++++++-
 4 files changed, 198 insertions(+), 1 deletion(-)
 create mode 100644 libavcodec/atrac3plus_parser.c

Comments

Michael Niedermayer Jan. 2, 2018, 9:40 p.m. UTC | #1
On Mon, Jan 01, 2018 at 08:32:22PM +1100, misty@brew.sh wrote:
> From: Maxim Poliakovski <maximumspatium@googlemail.com>
> 
> Changes by Misty De Meo <mistydemeo@gmail.com>:
> 
> atrac3plus_parser: remove return statements for invalid data
> 
> atrac3plus_parser: use libavcodec's oma
> 
> atrac3plus_parser: pass along unexpected data unaltered
> 
> atrac3plus_parser: adjust bytes_remain type
> 
> Change by Michael "Bazz" Bazzinotti <mbazzinotti@gmail.com>:
> 
> atrac3plus_parser: don't always fail video for "2nd frame portion found"
> 
> Signed-off-by: Misty De Meo <mistydemeo@gmail.com>
> 

> wip parser

so this is unfinished code that work in progress ?
if so, what is missing ?
if not, then please write a better commit message

[...]
Jan Ekström Jan. 4, 2018, 6:22 a.m. UTC | #2
On Mon, Jan 1, 2018 at 11:32 AM,  <misty@brew.sh> wrote:
> ...
> +        avctx->sample_rate    = ctx->sample_rate;
> +        avctx->block_align    = ctx->frame_size;
> +        avctx->bit_rate       = ctx->sample_rate * ctx->frame_size * 8 / 2048;
> +        avctx->channels       = oma_chid_to_num_channels[ctx->channel_id - 1];
> +        avctx->channel_layout = oma_chid_to_native_layout[ctx->channel_id - 1];
> +
> ...

Hi,

I had at one point looked into working on this after the initial patch
set failed to get merged as the AVCodecContext was modified in a
parser and people were afraid about threading issues. Is this no
longer an issue?

Currently this design of the parser is based on the ATRAC3+ decoder's
design, which is built upon the WAV-like OMA format. That, in part,
expects the demuxer to have filled all this information for it, and
basically it would have required to either insert the headers utilized
in the MPEG-PS encapsulation to every packet with OMA demux (and make
the decoder not require these values in the AVCodecContext) - which
would break any remuxing if such would take place unless those would
be removed in the muxer, or the decoder would have to have two modes.
One for the OMA-like (no header in packets), and the other for MPEG-PS
encapsulation (header in each packet). I had asked on the -devel IRC
channel regarding how to do it the best way a few times but never got
a nice answer.

> ...
> +    } else if (!memcmp("PSMF00", buffer, 6)) {
> +        m->sony_psmf = 1;
> ...

Games for the Sony PS3 seem to utilize the header "PAMF00" for the
same purpose. I can share a sample on IRC (poke JEEB).

Best regards,
Jan
Misty De Meo Jan. 5, 2018, 10:52 a.m. UTC | #3
Sorry about that - that was the placeholder title of a commit I
squashed into the mpeg commit, and forgot to remove when rebasing.
I'll remove it from the commit message.
misty@brew.sh Jan. 5, 2018, 11:03 a.m. UTC | #4
> so this is unfinished code that work in progress ?
> if so, what is missing ?
> if not, then please write a better commit message

Sorry about that - that was the placeholder title of a commit I
squashed into the mpeg commit, and forgot to remove when rebasing.
I'll remove it from the commit message.
diff mbox

Patch

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index ca72138c02..e0e3f1ebac 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -977,6 +977,7 @@  OBJS-$(CONFIG_AAC_PARSER)              += aac_parser.o aac_ac3_parser.o \
                                           mpeg4audio.o
 OBJS-$(CONFIG_AC3_PARSER)              += ac3tab.o aac_ac3_parser.o
 OBJS-$(CONFIG_ADX_PARSER)              += adx_parser.o adx.o
+OBJS-$(CONFIG_ATRAC3P_PARSER)          += atrac3plus_parser.o
 OBJS-$(CONFIG_BMP_PARSER)              += bmp_parser.o
 OBJS-$(CONFIG_CAVSVIDEO_PARSER)        += cavs_parser.o
 OBJS-$(CONFIG_COOK_PARSER)             += cook_parser.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index ed1e7ab06e..81d5d2814a 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -623,6 +623,7 @@  static void register_all(void)
     REGISTER_PARSER(AAC_LATM,           aac_latm);
     REGISTER_PARSER(AC3,                ac3);
     REGISTER_PARSER(ADX,                adx);
+    REGISTER_PARSER(ATRAC3P,            atrac3p);
     REGISTER_PARSER(BMP,                bmp);
     REGISTER_PARSER(CAVSVIDEO,          cavsvideo);
     REGISTER_PARSER(COOK,               cook);
diff --git a/libavcodec/atrac3plus_parser.c b/libavcodec/atrac3plus_parser.c
new file mode 100644
index 0000000000..5aebdd5fbc
--- /dev/null
+++ b/libavcodec/atrac3plus_parser.c
@@ -0,0 +1,170 @@ 
+/*
+ * Copyright (C) 2016 Michael "Bazz" Bazzinotti <mbazzinotti@gmail.com>
+ * 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 "parser.h"
+#include "get_bits.h"
+#include "oma.h"
+
+typedef struct Atrac3PlusParseContext {
+    ParseContext pc;
+    uint8_t hdr[8];
+    int hdr_bytes_needed;
+    int sample_rate, channel_id, frame_size;
+    uint8_t got_bytes;
+} Atrac3PlusParseContext;
+
+static int parse_sound_frame_header(Atrac3PlusParseContext *c,
+                                    const uint8_t *buf)
+{
+    uint16_t atrac_config;
+
+    if (AV_RB16(buf) != 0x0FD0)
+        return AVERROR_INVALIDDATA;
+
+    atrac_config = AV_RB16(&buf[2]);
+    c->sample_rate = oma_srate_tab[(atrac_config >> 13) & 7] * 100;
+    c->channel_id  = (atrac_config >> 10) & 7;
+    c->frame_size  = ((atrac_config & 0x3FF) * 8) + 8;
+
+    if (!c->channel_id || !c->sample_rate || !c->frame_size)
+        return AVERROR_INVALIDDATA;
+
+    return 0;
+}
+
+static int ff_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;
+    uint16_t bytes_remain;
+    int frame_size, hdr_bytes = 8;
+    int next = 0;
+    int second_portion_found = 0;
+
+    if (s->flags & PARSER_FLAG_COMPLETE_FRAMES || !buf_size) {
+        next = buf_size;
+    } else {
+        if (buf_size >= 2) {
+            bytes_remain = AV_RB16(buf);
+
+            if (bytes_remain != 0xFD0) {
+                second_portion_found = bytes_remain && !ctx->pc.index && !ctx->hdr_bytes_needed;
+                /* Got something unexpected; either this means we got
+                   the second part of a frame and not the first part,
+                   or more likely these aren't ATRAC3+ packets in the
+                   format we're expecting. For example, it might mean
+                   the source data is from ATRAC3+ in RIFF WAVE, which
+                   doesn't use the same format as ATRAC3+ in MPEG.
+                   Just pass this along unaltered. */
+                if (second_portion_found && !ctx->got_bytes) {
+                    *poutbuf = buf;
+                    *poutbuf_size = buf_size;
+                    return buf_size;
+                }
+
+                next += 2;
+                buf  += 2;
+                buf_size -= 2;
+                hdr_buf = buf;
+
+                if (second_portion_found) {
+                        ctx->got_bytes = 0;
+                        goto process;
+                }
+
+                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 (bytes_remain) {
+                    if (buf_size < bytes_remain) {
+                        av_log(avctx, AV_LOG_ERROR,
+                               "Couldn't combine frame: bytes needed=%d, bytes supplied=%d\n",
+                               bytes_remain, buf_size);
+                    }
+
+                    next += bytes_remain;
+                    ff_combine_frame(&ctx->pc, bytes_remain, &buf, &buf_size);
+
+                    *poutbuf      = buf;
+                    *poutbuf_size = buf_size;
+                    return next;
+                }
+            }
+        }
+
+        if (buf_size < hdr_bytes) {
+            /* looks like 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");
+        }
+
+        avctx->sample_rate    = ctx->sample_rate;
+        avctx->block_align    = ctx->frame_size;
+        avctx->bit_rate       = ctx->sample_rate * ctx->frame_size * 8 / 2048;
+        avctx->channels       = oma_chid_to_num_channels[ctx->channel_id - 1];
+        avctx->channel_layout = oma_chid_to_native_layout[ctx->channel_id - 1];
+
+        next += hdr_bytes;
+        buf  += hdr_bytes;
+        buf_size -= hdr_bytes;
+        if (!buf_size)
+            ctx->got_bytes = 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;
+}
+
+AVCodecParser ff_atrac3p_parser = {
+    .codec_ids      = { AV_CODEC_ID_ATRAC3P },
+    .priv_data_size = sizeof(Atrac3PlusParseContext),
+    .parser_parse   = ff_atrac3p_parse,
+    .parser_close   = ff_parse_close,
+};
diff --git a/libavformat/mpeg.c b/libavformat/mpeg.c
index 50fe7a1a76..895c6fb231 100644
--- a/libavformat/mpeg.c
+++ b/libavformat/mpeg.c
@@ -128,6 +128,7 @@  typedef struct MpegDemuxContext {
     int sofdec;
     int dvd;
     int imkh_cctv;
+    int sony_psmf; // true if Play Station Movie file signature is present
 #if CONFIG_VOBSUB_DEMUXER
     AVFormatContext *sub_ctx;
     FFDemuxSubtitlesQueue q[32];
@@ -147,6 +148,8 @@  static int mpegps_read_header(AVFormatContext *s)
     avio_get_str(s->pb, 6, buffer, sizeof(buffer));
     if (!memcmp("IMKH", buffer, 4)) {
         m->imkh_cctv = 1;
+    } else if (!memcmp("PSMF00", buffer, 6)) {
+        m->sony_psmf = 1;
     } else if (!memcmp("Sofdec", buffer, 6)) {
         m->sofdec = 1;
     } else
@@ -441,7 +444,7 @@  redo:
         goto redo;
     }
 
-    if (startcode == PRIVATE_STREAM_1) {
+    if (startcode == PRIVATE_STREAM_1 && !m->sony_psmf) {
         startcode = avio_r8(s->pb);
         len--;
     }
@@ -541,6 +544,28 @@  redo:
         else
             request_probe= 1;
         type = AVMEDIA_TYPE_VIDEO;
+    } else if (startcode == PRIVATE_STREAM_1 && m->sony_psmf) {
+        uint8_t stream_id;
+
+        if (len < 2)
+            goto skip;
+        stream_id = avio_r8(s->pb);
+        avio_r8(s->pb); // skip padding
+        len -= 2;
+        if (!(stream_id & 0xF0)) { // seems like we got an ATRAC stream
+            /* check if an appropriate stream already exists */
+            for (i = 0; i < s->nb_streams; i++) {
+                st = s->streams[i];
+                if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO &&
+                    st->codec->codec_id == AV_CODEC_ID_ATRAC3P &&
+                    st->id - 0x1BD0 == (stream_id & 0xF))
+                    goto found;
+            }
+
+            startcode = 0x1BD0 + (stream_id & 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;