amr.c 8.35 KB
Newer Older
1
/*
2
 * amr file format
3
 * Copyright (c) 2001 FFmpeg project
4
 *
5 6 7
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
8 9
 * modify it under the terms of the GNU Lesser General Public
 * 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 14 15 16 17
 * 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
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 23 24
 */

/*
Write and read amr data according to RFC3267, http://www.ietf.org/rfc/rfc3267.txt?number=3267

25
Only mono files are supported.
26 27

*/
Justin Ruggles's avatar
Justin Ruggles committed
28 29

#include "libavutil/channel_layout.h"
30
#include "avformat.h"
31
#include "internal.h"
32

33 34 35 36 37
typedef struct {
    uint64_t cumulated_size;
    uint64_t block_count;
} AMRContext;

Martin Storsjö's avatar
Martin Storsjö committed
38 39
static const char AMR_header[]   = "#!AMR\n";
static const char AMRWB_header[] = "#!AMR-WB\n";
40

41 42 43 44 45 46 47
static const uint8_t amrnb_packed_size[16] = {
    13, 14, 16, 18, 20, 21, 27, 32, 6, 1, 1, 1, 1, 1, 1, 1
};
static const uint8_t amrwb_packed_size[16] = {
    18, 24, 33, 37, 41, 47, 51, 59, 61, 6, 1, 1, 1, 1, 1, 1
};

48
#if CONFIG_AMR_MUXER
49 50
static int amr_write_header(AVFormatContext *s)
{
Martin Storsjö's avatar
Martin Storsjö committed
51
    AVIOContext    *pb  = s->pb;
52
    AVCodecParameters *par = s->streams[0]->codecpar;
53 54 55

    s->priv_data = NULL;

56
    if (par->codec_id == AV_CODEC_ID_AMR_NB) {
57
        avio_write(pb, AMR_header,   sizeof(AMR_header)   - 1); /* magic number */
58
    } else if (par->codec_id == AV_CODEC_ID_AMR_WB) {
59
        avio_write(pb, AMRWB_header, sizeof(AMRWB_header) - 1); /* magic number */
Martin Storsjö's avatar
Martin Storsjö committed
60
    } else {
Michael Niedermayer's avatar
Michael Niedermayer committed
61
        return -1;
62
    }
63
    avio_flush(pb);
64 65 66
    return 0;
}

67
static int amr_write_packet(AVFormatContext *s, AVPacket *pkt)
68
{
69
    avio_write(s->pb, pkt->data, pkt->size);
70 71
    return 0;
}
72
#endif /* CONFIG_AMR_MUXER */
73 74 75

static int amr_probe(AVProbeData *p)
{
Martin Storsjö's avatar
Martin Storsjö committed
76 77 78
    // Only check for "#!AMR" which could be amr-wb, amr-nb.
    // This will also trigger multichannel files: "#!AMR_MC1.0\n" and
    // "#!AMR-WB_MC1.0\n" (not supported)
79

Martin Storsjö's avatar
Martin Storsjö committed
80
    if (!memcmp(p->buf, AMR_header, 5))
81 82 83 84 85 86
        return AVPROBE_SCORE_MAX;
    else
        return 0;
}

/* amr input */
87
static int amr_read_header(AVFormatContext *s)
88
{
89
    AVIOContext *pb = s->pb;
90
    AVStream *st;
91
    uint8_t header[9];
92

93
    avio_read(pb, header, 6);
94

95
    st = avformat_new_stream(s, NULL);
96
    if (!st)
97
        return AVERROR(ENOMEM);
Martin Storsjö's avatar
Martin Storsjö committed
98 99 100
    if (memcmp(header, AMR_header, 6)) {
        avio_read(pb, header + 6, 3);
        if (memcmp(header, AMRWB_header, 9)) {
101 102
            return -1;
        }
103

104 105 106
        st->codecpar->codec_tag   = MKTAG('s', 'a', 'w', 'b');
        st->codecpar->codec_id    = AV_CODEC_ID_AMR_WB;
        st->codecpar->sample_rate = 16000;
Martin Storsjö's avatar
Martin Storsjö committed
107
    } else {
108 109 110
        st->codecpar->codec_tag   = MKTAG('s', 'a', 'm', 'r');
        st->codecpar->codec_id    = AV_CODEC_ID_AMR_NB;
        st->codecpar->sample_rate = 8000;
111
    }
112 113 114 115
    st->codecpar->channels   = 1;
    st->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
    st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
    avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
116 117 118 119

    return 0;
}

Martin Storsjö's avatar
Martin Storsjö committed
120
static int amr_read_packet(AVFormatContext *s, AVPacket *pkt)
121
{
122
    AVCodecParameters *par = s->streams[0]->codecpar;
123
    int read, size = 0, toc, mode;
124
    int64_t pos = avio_tell(s->pb);
125
    AMRContext *amr = s->priv_data;
126

127
    if (avio_feof(s->pb)) {
128
        return AVERROR_EOF;
129
    }
130

131
    // FIXME this is wrong, this should rather be in an AVParser
Martin Storsjö's avatar
Martin Storsjö committed
132
    toc  = avio_r8(s->pb);
133
    mode = (toc >> 3) & 0x0F;
134

135
    if (par->codec_id == AV_CODEC_ID_AMR_NB) {
136
        size = amrnb_packed_size[mode];
137
    } else if (par->codec_id == AV_CODEC_ID_AMR_WB) {
138
        size = amrwb_packed_size[mode];
139
    }
140

Martin Storsjö's avatar
Martin Storsjö committed
141
    if (!size || av_new_packet(pkt, size))
142
        return AVERROR(EIO);
143

144 145 146
    if (amr->cumulated_size < UINT64_MAX - size) {
        amr->cumulated_size += size;
        /* Both AMR formats have 50 frames per second */
147
        s->streams[0]->codecpar->bit_rate = amr->cumulated_size / ++amr->block_count * 8 * 50;
148
    }
149

150
    pkt->stream_index = 0;
Martin Storsjö's avatar
Martin Storsjö committed
151 152
    pkt->pos          = pos;
    pkt->data[0]      = toc;
153
    pkt->duration     = par->codec_id == AV_CODEC_ID_AMR_NB ? 160 : 320;
Martin Storsjö's avatar
Martin Storsjö committed
154
    read              = avio_read(s->pb, pkt->data + 1, size - 1);
155

Martin Storsjö's avatar
Martin Storsjö committed
156
    if (read != size - 1) {
157
        av_packet_unref(pkt);
158 159
        if (read < 0)
            return read;
160
        return AVERROR(EIO);
161 162
    }

Michael Niedermayer's avatar
Michael Niedermayer committed
163
    return 0;
164 165
}

166
#if CONFIG_AMR_DEMUXER
167
AVInputFormat ff_amr_demuxer = {
168
    .name           = "amr",
169
    .long_name      = NULL_IF_CONFIG_SMALL("3GPP AMR"),
170
    .priv_data_size = sizeof(AMRContext),
171 172 173
    .read_probe     = amr_probe,
    .read_header    = amr_read_header,
    .read_packet    = amr_read_packet,
174
    .flags          = AVFMT_GENERIC_INDEX,
175
};
176
#endif
177

178 179 180
#if CONFIG_AMRNB_DEMUXER
static int amrnb_probe(AVProbeData *p)
{
181
    int mode, i = 0, valid = 0, invalid = 0;
182 183 184 185 186
    const uint8_t *b = p->buf;

    while (i < p->buf_size) {
        mode = b[i] >> 3 & 0x0F;
        if (mode < 9 && (b[i] & 0x4) == 0x4) {
187
            int last = b[i];
188 189 190 191 192 193 194 195 196 197 198
            int size = amrnb_packed_size[mode];
            while (size--) {
                if (b[++i] != last)
                    break;
            }
            if (size > 0) {
                valid++;
                i += size;
            }
        } else {
            valid = 0;
199
            invalid++;
200 201 202
            i++;
        }
    }
203
    if (valid > 100 && valid >> 4 > invalid)
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
        return AVPROBE_SCORE_EXTENSION / 2 + 1;
    return 0;
}

static int amrnb_read_header(AVFormatContext *s)
{
    AVStream *st = avformat_new_stream(s, NULL);
    if (!st)
        return AVERROR(ENOMEM);
    st->codecpar->codec_id       = AV_CODEC_ID_AMR_NB;
    st->codecpar->sample_rate    = 8000;
    st->codecpar->channels       = 1;
    st->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
    st->codecpar->codec_type     = AVMEDIA_TYPE_AUDIO;
    avpriv_set_pts_info(st, 64, 1, 8000);

    return 0;
}

AVInputFormat ff_amrnb_demuxer = {
    .name           = "amrnb",
    .long_name      = NULL_IF_CONFIG_SMALL("raw AMR-NB"),
    .priv_data_size = sizeof(AMRContext),
    .read_probe     = amrnb_probe,
    .read_header    = amrnb_read_header,
    .read_packet    = amr_read_packet,
    .flags          = AVFMT_GENERIC_INDEX,
};
#endif

#if CONFIG_AMRWB_DEMUXER
static int amrwb_probe(AVProbeData *p)
{
237
    int mode, i = 0, valid = 0, invalid = 0;
238 239 240 241 242
    const uint8_t *b = p->buf;

    while (i < p->buf_size) {
        mode = b[i] >> 3 & 0x0F;
        if (mode < 10 && (b[i] & 0x4) == 0x4) {
243
            int last = b[i];
244 245 246 247 248 249 250 251 252 253 254
            int size = amrwb_packed_size[mode];
            while (size--) {
                if (b[++i] != last)
                    break;
            }
            if (size > 0) {
                valid++;
                i += size;
            }
        } else {
            valid = 0;
255
            invalid++;
256 257 258
            i++;
        }
    }
259 260
    if (valid > 100 && valid >> 4 > invalid)
        return AVPROBE_SCORE_EXTENSION / 2 + 1;
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
    return 0;
}

static int amrwb_read_header(AVFormatContext *s)
{
    AVStream *st = avformat_new_stream(s, NULL);
    if (!st)
        return AVERROR(ENOMEM);
    st->codecpar->codec_id       = AV_CODEC_ID_AMR_WB;
    st->codecpar->sample_rate    = 16000;
    st->codecpar->channels       = 1;
    st->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
    st->codecpar->codec_type     = AVMEDIA_TYPE_AUDIO;
    avpriv_set_pts_info(st, 64, 1, 16000);

    return 0;
}

AVInputFormat ff_amrwb_demuxer = {
    .name           = "amrwb",
    .long_name      = NULL_IF_CONFIG_SMALL("raw AMR-WB"),
    .priv_data_size = sizeof(AMRContext),
    .read_probe     = amrwb_probe,
    .read_header    = amrwb_read_header,
    .read_packet    = amr_read_packet,
    .flags          = AVFMT_GENERIC_INDEX,
};
#endif

290
#if CONFIG_AMR_MUXER
291
AVOutputFormat ff_amr_muxer = {
292
    .name              = "amr",
293
    .long_name         = NULL_IF_CONFIG_SMALL("3GPP AMR"),
294 295
    .mime_type         = "audio/amr",
    .extensions        = "amr",
296 297
    .audio_codec       = AV_CODEC_ID_AMR_NB,
    .video_codec       = AV_CODEC_ID_NONE,
298 299
    .write_header      = amr_write_header,
    .write_packet      = amr_write_packet,
300
    .flags             = AVFMT_NOTIMESTAMPS,
301
};
302
#endif