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

Submitted by misty@brew.sh on Jan. 5, 2018, 11:34 a.m.

Details

Message ID 20180105113448.13917-4-misty@brew.sh
State New
Headers show

Commit Message

misty@brew.sh Jan. 5, 2018, 11:34 a.m.
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>
---
 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

Patch hide | download patch | download mbox

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index cfacd6b70c..e3f7afbfe7 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;