You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
4.0KB

  1. /*
  2. * WavPack muxer
  3. * Copyright (c) 2012 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. #include "libavutil/intreadwrite.h"
  22. #include "avformat.h"
  23. #include "internal.h"
  24. #include "avio_internal.h"
  25. #include "apetag.h"
  26. #define WV_EXTRA_SIZE 12
  27. #define WV_END_BLOCK 0x1000
  28. typedef struct{
  29. uint32_t duration;
  30. } WVMuxContext;
  31. static int write_header(AVFormatContext *s)
  32. {
  33. AVCodecContext *codec = s->streams[0]->codec;
  34. if (s->nb_streams > 1) {
  35. av_log(s, AV_LOG_ERROR, "only one stream is supported\n");
  36. return AVERROR(EINVAL);
  37. }
  38. if (codec->codec_id != AV_CODEC_ID_WAVPACK) {
  39. av_log(s, AV_LOG_ERROR, "unsupported codec\n");
  40. return AVERROR(EINVAL);
  41. }
  42. if (codec->extradata_size > 0) {
  43. av_log_missing_feature(s, "remuxing from matroska container", 0);
  44. return AVERROR_PATCHWELCOME;
  45. }
  46. avpriv_set_pts_info(s->streams[0], 64, 1, codec->sample_rate);
  47. return 0;
  48. }
  49. static int write_packet(AVFormatContext *s, AVPacket *pkt)
  50. {
  51. WVMuxContext *wc = s->priv_data;
  52. AVCodecContext *codec = s->streams[0]->codec;
  53. AVIOContext *pb = s->pb;
  54. uint64_t size;
  55. uint32_t flags;
  56. uint32_t left = pkt->size;
  57. uint8_t *ptr = pkt->data;
  58. int off = codec->channels > 2 ? 4 : 0;
  59. /* FIXME: Simplify decoder/demuxer so bellow code can support midstream
  60. * change of stream parameters */
  61. wc->duration += pkt->duration;
  62. ffio_wfourcc(pb, "wvpk");
  63. if (off) {
  64. size = AV_RL32(pkt->data);
  65. if (size <= 12)
  66. return AVERROR_INVALIDDATA;
  67. size -= 12;
  68. } else {
  69. size = pkt->size;
  70. }
  71. if (size + off > left)
  72. return AVERROR_INVALIDDATA;
  73. avio_wl32(pb, size + 12);
  74. avio_wl16(pb, 0x410);
  75. avio_w8(pb, 0);
  76. avio_w8(pb, 0);
  77. avio_wl32(pb, -1);
  78. avio_wl32(pb, pkt->pts);
  79. ptr += off; left -= off;
  80. flags = AV_RL32(ptr + 4);
  81. avio_write(pb, ptr, size);
  82. ptr += size; left -= size;
  83. while (!(flags & WV_END_BLOCK) &&
  84. (left >= 4 + WV_EXTRA_SIZE)) {
  85. ffio_wfourcc(pb, "wvpk");
  86. size = AV_RL32(ptr);
  87. ptr += 4; left -= 4;
  88. if (size < 24 || size - 24 > left)
  89. return AVERROR_INVALIDDATA;
  90. avio_wl32(pb, size);
  91. avio_wl16(pb, 0x410);
  92. avio_w8(pb, 0);
  93. avio_w8(pb, 0);
  94. avio_wl32(pb, -1);
  95. avio_wl32(pb, pkt->pts);
  96. flags = AV_RL32(ptr + 4);
  97. avio_write(pb, ptr, WV_EXTRA_SIZE);
  98. ptr += WV_EXTRA_SIZE; left -= WV_EXTRA_SIZE;
  99. avio_write(pb, ptr, size - 24);
  100. ptr += size - 24; left -= size - 24;
  101. }
  102. avio_flush(pb);
  103. return 0;
  104. }
  105. static int write_trailer(AVFormatContext *s)
  106. {
  107. WVMuxContext *wc = s->priv_data;
  108. AVIOContext *pb = s->pb;
  109. ff_ape_write(s);
  110. if (pb->seekable) {
  111. avio_seek(pb, 12, SEEK_SET);
  112. avio_wl32(pb, wc->duration);
  113. avio_flush(pb);
  114. }
  115. return 0;
  116. }
  117. AVOutputFormat ff_wv_muxer = {
  118. .name = "wv",
  119. .long_name = NULL_IF_CONFIG_SMALL("WavPack"),
  120. .priv_data_size = sizeof(WVMuxContext),
  121. .extensions = "wv",
  122. .audio_codec = AV_CODEC_ID_WAVPACK,
  123. .video_codec = AV_CODEC_ID_NONE,
  124. .write_header = write_header,
  125. .write_packet = write_packet,
  126. .write_trailer = write_trailer,
  127. };