Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * AST muxer
  3.  * Copyright (c) 2012 James Almer
  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 "avformat.h"
  23. #include "avio_internal.h"
  24. #include "internal.h"
  25. #include "ast.h"
  26. #include "libavutil/mathematics.h"
  27. #include "libavutil/opt.h"
  28.  
  29. typedef struct ASTMuxContext {
  30.     AVClass *class;
  31.     int64_t  size;
  32.     int64_t  samples;
  33.     int64_t  loopstart;
  34.     int64_t  loopend;
  35.     int      fbs;
  36. } ASTMuxContext;
  37.  
  38. #define CHECK_LOOP(type) \
  39.     if (ast->loop ## type > 0) { \
  40.         ast->loop ## type = av_rescale_rnd(ast->loop ## type, enc->sample_rate, 1000, AV_ROUND_DOWN); \
  41.         if (ast->loop ## type < 0 || ast->loop ## type > UINT_MAX) { \
  42.             av_log(s, AV_LOG_ERROR, "Invalid loop" #type " value\n"); \
  43.             return AVERROR(EINVAL);  \
  44.         } \
  45.     }
  46.  
  47. static int ast_write_header(AVFormatContext *s)
  48. {
  49.     ASTMuxContext *ast = s->priv_data;
  50.     AVIOContext *pb = s->pb;
  51.     AVCodecContext *enc;
  52.     unsigned int codec_tag;
  53.  
  54.     if (s->nb_streams == 1) {
  55.         enc = s->streams[0]->codec;
  56.     } else {
  57.         av_log(s, AV_LOG_ERROR, "only one stream is supported\n");
  58.         return AVERROR(EINVAL);
  59.     }
  60.  
  61.     if (enc->codec_id == AV_CODEC_ID_ADPCM_AFC) {
  62.         av_log(s, AV_LOG_ERROR, "muxing ADPCM AFC is not implemented\n");
  63.         return AVERROR_PATCHWELCOME;
  64.     }
  65.  
  66.     codec_tag = ff_codec_get_tag(ff_codec_ast_tags, enc->codec_id);
  67.     if (!codec_tag) {
  68.         av_log(s, AV_LOG_ERROR, "unsupported codec\n");
  69.         return AVERROR(EINVAL);
  70.     }
  71.  
  72.     if (ast->loopend > 0 && ast->loopstart >= ast->loopend) {
  73.         av_log(s, AV_LOG_ERROR, "loopend can't be less or equal to loopstart\n");
  74.         return AVERROR(EINVAL);
  75.     }
  76.  
  77.     /* Convert milliseconds to samples */
  78.     CHECK_LOOP(start)
  79.     CHECK_LOOP(end)
  80.  
  81.     ffio_wfourcc(pb, "STRM");
  82.  
  83.     ast->size = avio_tell(pb);
  84.     avio_wb32(pb, 0); /* File size minus header */
  85.     avio_wb16(pb, codec_tag);
  86.     avio_wb16(pb, 16); /* Bit depth */
  87.     avio_wb16(pb, enc->channels);
  88.     avio_wb16(pb, 0); /* Loop flag */
  89.     avio_wb32(pb, enc->sample_rate);
  90.  
  91.     ast->samples = avio_tell(pb);
  92.     avio_wb32(pb, 0); /* Number of samples */
  93.     avio_wb32(pb, 0); /* Loopstart */
  94.     avio_wb32(pb, 0); /* Loopend */
  95.     avio_wb32(pb, 0); /* Size of first block */
  96.  
  97.     /* Unknown */
  98.     avio_wb32(pb, 0);
  99.     avio_wl32(pb, 0x7F);
  100.     avio_wb64(pb, 0);
  101.     avio_wb64(pb, 0);
  102.     avio_wb32(pb, 0);
  103.  
  104.     avio_flush(pb);
  105.  
  106.     return 0;
  107. }
  108.  
  109. static int ast_write_packet(AVFormatContext *s, AVPacket *pkt)
  110. {
  111.     AVIOContext *pb = s->pb;
  112.     ASTMuxContext *ast = s->priv_data;
  113.     AVCodecContext *enc = s->streams[0]->codec;
  114.     int size = pkt->size / enc->channels;
  115.  
  116.     if (enc->frame_number == 1)
  117.         ast->fbs = size;
  118.  
  119.     ffio_wfourcc(pb, "BLCK");
  120.     avio_wb32(pb, size); /* Block size */
  121.  
  122.     /* padding */
  123.     avio_wb64(pb, 0);
  124.     avio_wb64(pb, 0);
  125.     avio_wb64(pb, 0);
  126.  
  127.     avio_write(pb, pkt->data, pkt->size);
  128.  
  129.     return 0;
  130. }
  131.  
  132. static int ast_write_trailer(AVFormatContext *s)
  133. {
  134.     AVIOContext *pb = s->pb;
  135.     ASTMuxContext *ast = s->priv_data;
  136.     AVCodecContext *enc = s->streams[0]->codec;
  137.     int64_t file_size = avio_tell(pb);
  138.     int64_t samples = (file_size - 64 - (32 * enc->frame_number)) / enc->block_align; /* PCM_S16BE_PLANAR */
  139.  
  140.     av_log(s, AV_LOG_DEBUG, "total samples: %"PRId64"\n", samples);
  141.  
  142.     if (s->pb->seekable) {
  143.         /* Number of samples */
  144.         avio_seek(pb, ast->samples, SEEK_SET);
  145.         avio_wb32(pb, samples);
  146.  
  147.         /* Loopstart if provided */
  148.         if (ast->loopstart > 0) {
  149.         if (ast->loopstart >= samples) {
  150.             av_log(s, AV_LOG_WARNING, "Loopstart value is out of range and will be ignored\n");
  151.             ast->loopstart = -1;
  152.             avio_skip(pb, 4);
  153.         } else
  154.         avio_wb32(pb, ast->loopstart);
  155.         } else
  156.             avio_skip(pb, 4);
  157.  
  158.         /* Loopend if provided. Otherwise number of samples again */
  159.         if (ast->loopend && ast->loopstart >= 0) {
  160.             if (ast->loopend > samples) {
  161.                 av_log(s, AV_LOG_WARNING, "Loopend value is out of range and will be ignored\n");
  162.                 ast->loopend = samples;
  163.             }
  164.             avio_wb32(pb, ast->loopend);
  165.         } else {
  166.             avio_wb32(pb, samples);
  167.         }
  168.  
  169.         /* Size of first block */
  170.         avio_wb32(pb, ast->fbs);
  171.  
  172.         /* File size minus header */
  173.         avio_seek(pb, ast->size, SEEK_SET);
  174.         avio_wb32(pb, file_size - 64);
  175.  
  176.         /* Loop flag */
  177.         if (ast->loopstart >= 0) {
  178.             avio_skip(pb, 6);
  179.             avio_wb16(pb, 0xFFFF);
  180.         }
  181.  
  182.         avio_seek(pb, file_size, SEEK_SET);
  183.         avio_flush(pb);
  184.     }
  185.     return 0;
  186. }
  187.  
  188. #define OFFSET(obj) offsetof(ASTMuxContext, obj)
  189. static const AVOption options[] = {
  190.   { "loopstart", "Loopstart position in milliseconds.", OFFSET(loopstart), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
  191.   { "loopend",   "Loopend position in milliseconds.",   OFFSET(loopend),   AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
  192.   { NULL },
  193. };
  194.  
  195. static const AVClass ast_muxer_class = {
  196.     .class_name = "AST muxer",
  197.     .item_name  = av_default_item_name,
  198.     .option     = options,
  199.     .version    = LIBAVUTIL_VERSION_INT,
  200. };
  201.  
  202. AVOutputFormat ff_ast_muxer = {
  203.     .name              = "ast",
  204.     .long_name         = NULL_IF_CONFIG_SMALL("AST (Audio Stream)"),
  205.     .extensions        = "ast",
  206.     .priv_data_size    = sizeof(ASTMuxContext),
  207.     .audio_codec       = AV_CODEC_ID_PCM_S16BE_PLANAR,
  208.     .video_codec       = AV_CODEC_ID_NONE,
  209.     .write_header      = ast_write_header,
  210.     .write_packet      = ast_write_packet,
  211.     .write_trailer     = ast_write_trailer,
  212.     .priv_class        = &ast_muxer_class,
  213.     .codec_tag         = (const AVCodecTag* const []){ff_codec_ast_tags, 0},
  214. };
  215.