summaryrefslogtreecommitdiff
path: root/libavformat/sdsdec.c (plain)
blob: 081bb4ca2d8740e73f1a747b69f7648087710862
1/*
2 * MIDI Sample Dump Standard format demuxer
3 * Copyright (c) 2017 Paul B Mahol
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include "libavutil/intreadwrite.h"
23#include "avformat.h"
24#include "internal.h"
25
26typedef struct SDSContext {
27 uint8_t data[120];
28 int bit_depth;
29 int size;
30 void (*read_block)(const uint8_t *src, uint32_t *dst);
31} SDSContext;
32
33static int sds_probe(AVProbeData *p)
34{
35 if (AV_RB32(p->buf) == 0xF07E0001 && p->buf[20] == 0xF7 &&
36 p->buf[6] >= 8 && p->buf[6] <= 28)
37 return AVPROBE_SCORE_EXTENSION;
38 return 0;
39}
40
41static void byte2_read(const uint8_t *src, uint32_t *dst)
42{
43 int i;
44
45 for (i = 0; i < 120; i += 2) {
46 unsigned sample = (src[i + 0] << 25) + (src[i + 1] << 18);
47
48 dst[i / 2] = sample;
49 }
50}
51
52static void byte3_read(const uint8_t *src, uint32_t *dst)
53{
54 int i;
55
56 for (i = 0; i < 120; i += 3) {
57 unsigned sample;
58
59 sample = (src[i + 0] << 25) | (src[i + 1] << 18) | (src[i + 2] << 11);
60 dst[i / 3] = sample;
61 }
62}
63
64static void byte4_read(const uint8_t *src, uint32_t *dst)
65{
66 int i;
67
68 for (i = 0; i < 120; i += 4) {
69 unsigned sample;
70
71 sample = (src[i + 0] << 25) | (src[i + 1] << 18) | (src[i + 2] << 11) | (src[i + 3] << 4);
72 dst[i / 4] = sample;
73 }
74}
75
76#define SDS_3BYTE_TO_INT_DECODE(x) (((x) & 0x7F) | (((x) & 0x7F00) >> 1) | (((x) & 0x7F0000) >> 2))
77
78static int sds_read_header(AVFormatContext *ctx)
79{
80 SDSContext *s = ctx->priv_data;
81 unsigned sample_period;
82 AVIOContext *pb = ctx->pb;
83 AVStream *st;
84
85 st = avformat_new_stream(ctx, NULL);
86 if (!st)
87 return AVERROR(ENOMEM);
88
89 avio_skip(pb, 4);
90 avio_skip(pb, 2);
91
92 s->bit_depth = avio_r8(pb);
93 if (s->bit_depth < 8 || s->bit_depth > 28)
94 return AVERROR_INVALIDDATA;
95
96 if (s->bit_depth < 14) {
97 s->read_block = byte2_read;
98 s->size = 60 * 4;
99 } else if (s->bit_depth < 21) {
100 s->read_block = byte3_read;
101 s->size = 40 * 4;
102 } else {
103 s->read_block = byte4_read;
104 s->size = 30 * 4;
105 }
106 st->codecpar->codec_id = AV_CODEC_ID_PCM_U32LE;
107
108 sample_period = avio_rl24(pb);
109 sample_period = SDS_3BYTE_TO_INT_DECODE(sample_period);
110 avio_skip(pb, 11);
111
112 st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
113 st->codecpar->channels = 1;
114 st->codecpar->sample_rate = sample_period ? 1000000000 / sample_period : 16000;
115 st->duration = (avio_size(pb) - 21) / (127) * s->size / 4;
116
117 avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
118
119 return 0;
120}
121
122static int sds_read_packet(AVFormatContext *ctx, AVPacket *pkt)
123{
124 SDSContext *s = ctx->priv_data;
125 AVIOContext *pb = ctx->pb;
126 int64_t pos;
127 int ret;
128
129 if (avio_feof(pb))
130 return AVERROR_EOF;
131
132 pos = avio_tell(pb);
133 if (avio_rb16(pb) != 0xF07E)
134 return AVERROR_INVALIDDATA;
135 avio_skip(pb, 3);
136
137 ret = av_new_packet(pkt, s->size);
138 if (ret < 0)
139 return ret;
140
141 ret = avio_read(pb, s->data, 120);
142
143 s->read_block(s->data, (uint32_t *)pkt->data);
144
145 avio_skip(pb, 1); // checksum
146 if (avio_r8(pb) != 0xF7)
147 return AVERROR_INVALIDDATA;
148
149 pkt->flags &= ~AV_PKT_FLAG_CORRUPT;
150 pkt->stream_index = 0;
151 pkt->pos = pos;
152
153 return ret;
154}
155
156AVInputFormat ff_sds_demuxer = {
157 .name = "sds",
158 .long_name = NULL_IF_CONFIG_SMALL("MIDI Sample Dump Standard"),
159 .priv_data_size = sizeof(SDSContext),
160 .read_probe = sds_probe,
161 .read_header = sds_read_header,
162 .read_packet = sds_read_packet,
163 .extensions = "sds",
164 .flags = AVFMT_GENERIC_INDEX,
165};
166