Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * This file is part of FFmpeg. | |
3 | * | |
4 | * FFmpeg is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU Lesser General Public | |
6 | * License as published by the Free Software Foundation; either | |
7 | * version 2.1 of the License, or (at your option) any later version. | |
8 | * | |
9 | * FFmpeg is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | * Lesser General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU Lesser General Public | |
15 | * License along with FFmpeg; if not, write to the Free Software | |
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
17 | */ | |
18 | ||
19 | #include <wavpack/wavpack.h> | |
20 | #include <string.h> | |
21 | ||
22 | #include "libavutil/attributes.h" | |
23 | #include "libavutil/opt.h" | |
24 | #include "libavutil/samplefmt.h" | |
25 | ||
26 | #include "audio_frame_queue.h" | |
27 | #include "avcodec.h" | |
28 | #include "internal.h" | |
29 | ||
30 | #define WV_DEFAULT_BLOCK_SIZE 32768 | |
31 | ||
32 | typedef struct LibWavpackContext { | |
33 | const AVClass *class; | |
34 | WavpackContext *wv; | |
35 | AudioFrameQueue afq; | |
36 | ||
37 | AVPacket *pkt; | |
38 | int user_size; | |
39 | ||
40 | int got_output; | |
41 | } LibWavpackContext; | |
42 | ||
43 | static int wavpack_encode_frame(AVCodecContext *avctx, AVPacket *pkt, | |
44 | const AVFrame *frame, int *got_output) | |
45 | { | |
46 | LibWavpackContext *s = avctx->priv_data; | |
47 | int ret; | |
48 | ||
49 | s->got_output = 0; | |
50 | s->pkt = pkt; | |
51 | s->user_size = pkt->size; | |
52 | ||
53 | if (frame) { | |
54 | ret = ff_af_queue_add(&s->afq, frame); | |
55 | if (ret < 0) | |
56 | return ret; | |
57 | ||
58 | ret = WavpackPackSamples(s->wv, (int32_t*)frame->data[0], frame->nb_samples); | |
59 | if (!ret) { | |
60 | av_log(avctx, AV_LOG_ERROR, "Error encoding a frame: %s\n", | |
61 | WavpackGetErrorMessage(s->wv)); | |
62 | return AVERROR_UNKNOWN; | |
63 | } | |
64 | } | |
65 | ||
66 | if (!s->got_output && | |
67 | (!frame || frame->nb_samples < avctx->frame_size)) { | |
68 | ret = WavpackFlushSamples(s->wv); | |
69 | if (!ret) { | |
70 | av_log(avctx, AV_LOG_ERROR, "Error flushing the encoder: %s\n", | |
71 | WavpackGetErrorMessage(s->wv)); | |
72 | return AVERROR_UNKNOWN; | |
73 | } | |
74 | } | |
75 | ||
76 | if (s->got_output) { | |
77 | ff_af_queue_remove(&s->afq, avctx->frame_size, &pkt->pts, &pkt->duration); | |
78 | *got_output = 1; | |
79 | } | |
80 | ||
81 | return 0; | |
82 | } | |
83 | ||
84 | static int encode_callback(void *id, void *data, int32_t count) | |
85 | { | |
86 | AVCodecContext *avctx = id; | |
87 | LibWavpackContext *s = avctx->priv_data; | |
88 | int ret, offset = s->pkt->size; | |
89 | ||
90 | if (s->user_size) { | |
91 | if (s->user_size - count < s->pkt->size) { | |
92 | av_log(avctx, AV_LOG_ERROR, "Provided packet too small.\n"); | |
93 | return 0; | |
94 | } | |
95 | s->pkt->size += count; | |
96 | } else { | |
97 | ret = av_grow_packet(s->pkt, count); | |
98 | if (ret < 0) { | |
99 | av_log(avctx, AV_LOG_ERROR, "Error allocating output packet.\n"); | |
100 | return 0; | |
101 | } | |
102 | } | |
103 | ||
104 | memcpy(s->pkt->data + offset, data, count); | |
105 | ||
106 | s->got_output = 1; | |
107 | ||
108 | return 1; | |
109 | } | |
110 | ||
111 | static av_cold int wavpack_encode_init(AVCodecContext *avctx) | |
112 | { | |
113 | LibWavpackContext *s = avctx->priv_data; | |
114 | WavpackConfig config = { 0 }; | |
115 | int ret; | |
116 | ||
117 | s->wv = WavpackOpenFileOutput(encode_callback, avctx, NULL); | |
118 | if (!s->wv) { | |
119 | av_log(avctx, AV_LOG_ERROR, "Error allocating the encoder.\n"); | |
120 | return AVERROR(ENOMEM); | |
121 | } | |
122 | ||
123 | if (!avctx->frame_size) | |
124 | avctx->frame_size = WV_DEFAULT_BLOCK_SIZE; | |
125 | ||
126 | config.bytes_per_sample = 4; | |
127 | config.bits_per_sample = 32; | |
128 | config.block_samples = avctx->frame_size; | |
129 | config.channel_mask = avctx->channel_layout; | |
130 | config.num_channels = avctx->channels; | |
131 | config.sample_rate = avctx->sample_rate; | |
132 | ||
133 | if (avctx->compression_level != FF_COMPRESSION_DEFAULT) { | |
134 | if (avctx->compression_level >= 3) { | |
135 | config.flags |= CONFIG_VERY_HIGH_FLAG; | |
136 | ||
137 | if (avctx->compression_level >= 8) | |
138 | config.xmode = 6; | |
139 | else if (avctx->compression_level >= 7) | |
140 | config.xmode = 5; | |
141 | else if (avctx->compression_level >= 6) | |
142 | config.xmode = 4; | |
143 | else if (avctx->compression_level >= 5) | |
144 | config.xmode = 3; | |
145 | else if (avctx->compression_level >= 4) | |
146 | config.xmode = 2; | |
147 | } else if (avctx->compression_level >= 2) | |
148 | config.flags |= CONFIG_HIGH_FLAG; | |
149 | else if (avctx->compression_level < 1) | |
150 | config.flags |= CONFIG_FAST_FLAG; | |
151 | } | |
152 | ||
153 | ret = WavpackSetConfiguration(s->wv, &config, -1); | |
154 | if (!ret) | |
155 | goto fail; | |
156 | ||
157 | ret = WavpackPackInit(s->wv); | |
158 | if (!ret) | |
159 | goto fail; | |
160 | ||
161 | ff_af_queue_init(avctx, &s->afq); | |
162 | ||
163 | return 0; | |
164 | ||
165 | fail: | |
166 | av_log(avctx, AV_LOG_ERROR, "Error configuring the encoder: %s.\n", | |
167 | WavpackGetErrorMessage(s->wv)); | |
168 | WavpackCloseFile(s->wv); | |
169 | return AVERROR_UNKNOWN; | |
170 | } | |
171 | ||
172 | static av_cold int wavpack_encode_close(AVCodecContext *avctx) | |
173 | { | |
174 | LibWavpackContext *s = avctx->priv_data; | |
175 | ||
176 | WavpackCloseFile(s->wv); | |
177 | ||
178 | ff_af_queue_close(&s->afq); | |
179 | ||
180 | return 0; | |
181 | } | |
182 | ||
183 | AVCodec ff_libwavpack_encoder = { | |
184 | .name = "libwavpack", | |
185 | .type = AVMEDIA_TYPE_AUDIO, | |
186 | .id = AV_CODEC_ID_WAVPACK, | |
187 | .priv_data_size = sizeof(LibWavpackContext), | |
188 | .init = wavpack_encode_init, | |
189 | .encode2 = wavpack_encode_frame, | |
190 | .close = wavpack_encode_close, | |
191 | .capabilities = CODEC_CAP_DELAY | CODEC_CAP_SMALL_LAST_FRAME, | |
192 | .sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S32, | |
193 | AV_SAMPLE_FMT_NONE }, | |
194 | }; |