3 * Copyright (c) 2012 James Almer
5 * This file is part of FFmpeg.
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.
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.
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
23 #include "avio_internal.h"
27 #include "libavutil/mathematics.h"
28 #include "libavutil/opt.h"
30 typedef struct ASTMuxContext
{
39 #define CHECK_LOOP(type) \
40 if (ast->loop ## type > 0) { \
41 ast->loop ## type = av_rescale_rnd(ast->loop ## type, par->sample_rate, 1000, AV_ROUND_DOWN); \
42 if (ast->loop ## type < 0 || ast->loop ## type > UINT_MAX) { \
43 av_log(s, AV_LOG_ERROR, "Invalid loop" #type " value\n"); \
44 return AVERROR(EINVAL); \
48 static int ast_write_header(AVFormatContext
*s
)
50 ASTMuxContext
*ast
= s
->priv_data
;
51 AVIOContext
*pb
= s
->pb
;
52 AVCodecParameters
*par
= s
->streams
[0]->codecpar
;
53 unsigned int codec_tag
;
55 if (par
->codec_id
== AV_CODEC_ID_ADPCM_AFC
) {
56 av_log(s
, AV_LOG_ERROR
, "muxing ADPCM AFC is not implemented\n");
57 return AVERROR_PATCHWELCOME
;
60 codec_tag
= ff_codec_get_tag(ff_codec_ast_tags
, par
->codec_id
);
62 av_log(s
, AV_LOG_ERROR
, "unsupported codec\n");
63 return AVERROR(EINVAL
);
66 if (ast
->loopend
> 0 && ast
->loopstart
>= ast
->loopend
) {
67 av_log(s
, AV_LOG_ERROR
, "loopend can't be less or equal to loopstart\n");
68 return AVERROR(EINVAL
);
71 /* Convert milliseconds to samples */
75 ffio_wfourcc(pb
, "STRM");
77 ast
->size
= avio_tell(pb
);
78 avio_wb32(pb
, 0); /* File size minus header */
79 avio_wb16(pb
, codec_tag
);
80 avio_wb16(pb
, 16); /* Bit depth */
81 avio_wb16(pb
, par
->ch_layout
.nb_channels
);
82 avio_wb16(pb
, 0); /* Loop flag */
83 avio_wb32(pb
, par
->sample_rate
);
85 ast
->samples
= avio_tell(pb
);
86 avio_wb32(pb
, 0); /* Number of samples */
87 avio_wb32(pb
, 0); /* Loopstart */
88 avio_wb32(pb
, 0); /* Loopend */
89 avio_wb32(pb
, 0); /* Size of first block */
101 static int ast_write_packet(AVFormatContext
*s
, AVPacket
*pkt
)
103 AVIOContext
*pb
= s
->pb
;
104 ASTMuxContext
*ast
= s
->priv_data
;
105 AVCodecParameters
*par
= s
->streams
[0]->codecpar
;
106 int size
= pkt
->size
/ par
->ch_layout
.nb_channels
;
108 if (s
->streams
[0]->nb_frames
== 0)
111 ffio_wfourcc(pb
, "BLCK");
112 avio_wb32(pb
, size
); /* Block size */
115 ffio_fill(pb
, 0, 24);
117 avio_write(pb
, pkt
->data
, pkt
->size
);
122 static int ast_write_trailer(AVFormatContext
*s
)
124 AVIOContext
*pb
= s
->pb
;
125 ASTMuxContext
*ast
= s
->priv_data
;
126 AVCodecParameters
*par
= s
->streams
[0]->codecpar
;
127 int64_t file_size
= avio_tell(pb
);
128 int64_t samples
= (file_size
- 64 - (32 * s
->streams
[0]->nb_frames
)) / par
->block_align
; /* PCM_S16BE_PLANAR */
130 av_log(s
, AV_LOG_DEBUG
, "total samples: %"PRId64
"\n", samples
);
132 if (s
->pb
->seekable
& AVIO_SEEKABLE_NORMAL
) {
133 /* Number of samples */
134 avio_seek(pb
, ast
->samples
, SEEK_SET
);
135 avio_wb32(pb
, samples
);
137 /* Loopstart if provided */
138 if (ast
->loopstart
> 0) {
139 if (ast
->loopstart
>= samples
) {
140 av_log(s
, AV_LOG_WARNING
, "Loopstart value is out of range and will be ignored\n");
144 avio_wb32(pb
, ast
->loopstart
);
150 /* Loopend if provided. Otherwise number of samples again */
151 if (ast
->loopend
&& ast
->loopstart
>= 0) {
152 if (ast
->loopend
> samples
) {
153 av_log(s
, AV_LOG_WARNING
, "Loopend value is out of range and will be ignored\n");
154 ast
->loopend
= samples
;
156 avio_wb32(pb
, ast
->loopend
);
158 avio_wb32(pb
, samples
);
161 /* Size of first block */
162 avio_wb32(pb
, ast
->fbs
);
164 /* File size minus header */
165 avio_seek(pb
, ast
->size
, SEEK_SET
);
166 avio_wb32(pb
, file_size
- 64);
169 if (ast
->loopstart
>= 0) {
171 avio_wb16(pb
, 0xFFFF);
174 avio_seek(pb
, file_size
, SEEK_SET
);
179 #define OFFSET(obj) offsetof(ASTMuxContext, obj)
180 static const AVOption options
[] = {
181 { "loopstart", "Loopstart position in milliseconds.", OFFSET(loopstart
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT_MAX
, AV_OPT_FLAG_ENCODING_PARAM
},
182 { "loopend", "Loopend position in milliseconds.", OFFSET(loopend
), AV_OPT_TYPE_INT64
, { .i64
= 0 }, 0, INT_MAX
, AV_OPT_FLAG_ENCODING_PARAM
},
186 static const AVClass ast_muxer_class
= {
187 .class_name
= "AST muxer",
188 .item_name
= av_default_item_name
,
190 .version
= LIBAVUTIL_VERSION_INT
,
193 const FFOutputFormat ff_ast_muxer
= {
195 .p
.long_name
= NULL_IF_CONFIG_SMALL("AST (Audio Stream)"),
196 .p
.extensions
= "ast",
197 .priv_data_size
= sizeof(ASTMuxContext
),
198 .p
.audio_codec
= AV_CODEC_ID_PCM_S16BE_PLANAR
,
199 .p
.video_codec
= AV_CODEC_ID_NONE
,
200 .p
.subtitle_codec
= AV_CODEC_ID_NONE
,
201 .flags_internal
= FF_OFMT_FLAG_MAX_ONE_OF_EACH
,
202 .write_header
= ast_write_header
,
203 .write_packet
= ast_write_packet
,
204 .write_trailer
= ast_write_trailer
,
205 .p
.priv_class
= &ast_muxer_class
,
206 .p
.codec_tag
= ff_ast_codec_tags_list
,