00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "avformat.h"
00023 #include "avio_internal.h"
00024 #include "internal.h"
00025 #include "ast.h"
00026 #include "libavutil/mathematics.h"
00027 #include "libavutil/opt.h"
00028
00029 typedef struct ASTMuxContext {
00030 AVClass *class;
00031 int64_t size;
00032 int64_t samples;
00033 int64_t loopstart;
00034 int64_t loopend;
00035 int fbs;
00036 } ASTMuxContext;
00037
00038 #define CHECK_LOOP(type) \
00039 if (ast->loop ## type) { \
00040 ast->loop ## type = av_rescale_q_rnd(ast->loop ## type, (AVRational){enc->sample_rate, 1}, (AVRational){1000, 1}, AV_ROUND_DOWN); \
00041 if (ast->loop ## type < 0 || ast->loop ## type > UINT_MAX) { \
00042 av_log(s, AV_LOG_ERROR, "Invalid loop" #type " value\n"); \
00043 return AVERROR(EINVAL); \
00044 } \
00045 }
00046
00047 static int ast_write_header(AVFormatContext *s)
00048 {
00049 ASTMuxContext *ast = s->priv_data;
00050 AVIOContext *pb = s->pb;
00051 AVCodecContext *enc;
00052 unsigned int codec_tag;
00053
00054 if (s->nb_streams == 1) {
00055 enc = s->streams[0]->codec;
00056 } else {
00057 av_log(s, AV_LOG_ERROR, "only one stream is supported\n");
00058 return AVERROR(EINVAL);
00059 }
00060
00061 if (enc->codec_id == AV_CODEC_ID_ADPCM_AFC) {
00062 av_log(s, AV_LOG_ERROR, "muxing ADPCM AFC is not implemented\n");
00063 return AVERROR_PATCHWELCOME;
00064 }
00065
00066 codec_tag = ff_codec_get_tag(ff_codec_ast_tags, enc->codec_id);
00067 if (!codec_tag) {
00068 av_log(s, AV_LOG_ERROR, "unsupported codec\n");
00069 return AVERROR(EINVAL);
00070 }
00071
00072 if (ast->loopstart && ast->loopend && ast->loopstart >= ast->loopend) {
00073 av_log(s, AV_LOG_ERROR, "loopend can't be less or equal to loopstart\n");
00074 return AVERROR(EINVAL);
00075 }
00076
00077
00078 CHECK_LOOP(start)
00079 CHECK_LOOP(end)
00080
00081 ffio_wfourcc(pb, "STRM");
00082
00083 ast->size = avio_tell(pb);
00084 avio_wb32(pb, 0);
00085 avio_wb16(pb, codec_tag);
00086 avio_wb16(pb, 16);
00087 avio_wb16(pb, enc->channels);
00088 avio_wb16(pb, 0xFFFF);
00089 avio_wb32(pb, enc->sample_rate);
00090
00091 ast->samples = avio_tell(pb);
00092 avio_wb32(pb, 0);
00093 avio_wb32(pb, 0);
00094 avio_wb32(pb, 0);
00095 avio_wb32(pb, 0);
00096
00097
00098 avio_wb32(pb, 0);
00099 avio_wl32(pb, 0x7F);
00100 avio_wb64(pb, 0);
00101 avio_wb64(pb, 0);
00102 avio_wb32(pb, 0);
00103
00104 avio_flush(pb);
00105
00106 return 0;
00107 }
00108
00109 static int ast_write_packet(AVFormatContext *s, AVPacket *pkt)
00110 {
00111 AVIOContext *pb = s->pb;
00112 ASTMuxContext *ast = s->priv_data;
00113 AVCodecContext *enc = s->streams[0]->codec;
00114 int size = pkt->size / enc->channels;
00115
00116 if (enc->frame_number == 1)
00117 ast->fbs = size;
00118
00119 ffio_wfourcc(pb, "BLCK");
00120 avio_wb32(pb, size);
00121
00122
00123 avio_wb64(pb, 0);
00124 avio_wb64(pb, 0);
00125 avio_wb64(pb, 0);
00126
00127 avio_write(pb, pkt->data, pkt->size);
00128
00129 return 0;
00130 }
00131
00132 static int ast_write_trailer(AVFormatContext *s)
00133 {
00134 AVIOContext *pb = s->pb;
00135 ASTMuxContext *ast = s->priv_data;
00136 AVCodecContext *enc = s->streams[0]->codec;
00137 int64_t file_size = avio_tell(pb);
00138 int64_t samples = (file_size - 64 - (32 * enc->frame_number)) / enc->block_align;
00139
00140 av_log(s, AV_LOG_DEBUG, "total samples: %"PRId64"\n", samples);
00141
00142 if (s->pb->seekable) {
00143
00144 avio_seek(pb, ast->size, SEEK_SET);
00145 avio_wb32(pb, file_size - 64);
00146
00147
00148 avio_seek(pb, ast->samples, SEEK_SET);
00149 avio_wb32(pb, samples);
00150
00151
00152 if (ast->loopstart && ast->loopstart >= samples) {
00153 av_log(s, AV_LOG_WARNING, "Loopstart value is out of range and will be ignored\n");
00154 ast->loopstart = 0;
00155 }
00156 avio_wb32(pb, ast->loopstart);
00157
00158
00159 if (ast->loopend) {
00160 if (ast->loopend > samples) {
00161 av_log(s, AV_LOG_WARNING, "Loopend value is out of range and will be ignored\n");
00162 ast->loopend = samples;
00163 }
00164 avio_wb32(pb, ast->loopend);
00165 } else {
00166 avio_wb32(pb, samples);
00167 }
00168
00169
00170 avio_wb32(pb, ast->fbs);
00171 avio_flush(pb);
00172 }
00173 return 0;
00174 }
00175
00176 #define OFFSET(obj) offsetof(ASTMuxContext, obj)
00177 static const AVOption options[] = {
00178 { "loopstart", "Loopstart position in milliseconds.", OFFSET(loopstart), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
00179 { "loopend", "Loopend position in milliseconds.", OFFSET(loopend), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
00180 { NULL },
00181 };
00182
00183 static const AVClass ast_muxer_class = {
00184 .class_name = "AST muxer",
00185 .item_name = av_default_item_name,
00186 .option = options,
00187 .version = LIBAVUTIL_VERSION_INT,
00188 };
00189
00190 AVOutputFormat ff_ast_muxer = {
00191 .name = "ast",
00192 .long_name = NULL_IF_CONFIG_SMALL("AST (Audio Stream)"),
00193 .extensions = "ast",
00194 .priv_data_size = sizeof(ASTMuxContext),
00195 .audio_codec = AV_CODEC_ID_PCM_S16BE_PLANAR,
00196 .video_codec = AV_CODEC_ID_NONE,
00197 .write_header = ast_write_header,
00198 .write_packet = ast_write_packet,
00199 .write_trailer = ast_write_trailer,
00200 .priv_class = &ast_muxer_class,
00201 .codec_tag = (const AVCodecTag* const []){ff_codec_ast_tags, 0},
00202 };