Mercurial > libavformat.hg
annotate oggenc.c @ 4037:5f65cbe25494 libavformat
Fix memleak caused by the fact that url_open_buf() allocates a context
when calling, but url_close_buf() doesn't free it. The better solution
is to not allocate it at all, init it with init_put_byte() and then
not have to close it at all. In the case where we do need to hold it
around for longer than within the function context, we allocate it with
av_alloc_put_byte() and free it with av_free() instead. Discussed in ML
thread "[PATCH] fix small memleak in rdt.c".
author | rbultje |
---|---|
date | Mon, 17 Nov 2008 14:23:20 +0000 |
parents | 549a09cf23fe |
children | 92117125e17c |
rev | line source |
---|---|
2731 | 1 /* |
2 * Ogg muxer | |
3 * Copyright (c) 2007 Baptiste Coudurier <baptiste dot coudurier at free dot fr> | |
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 | |
3286 | 22 #include "libavutil/crc.h" |
23 #include "libavcodec/xiph.h" | |
24 #include "libavcodec/bytestream.h" | |
2731 | 25 #include "avformat.h" |
26 | |
27 typedef struct { | |
28 int64_t duration; | |
29 unsigned page_counter; | |
30 uint8_t *header[3]; | |
31 int header_len[3]; | |
32 /** for theora granule */ | |
33 int kfgshift; | |
34 int64_t last_kf_pts; | |
35 int vrev; | |
3021 | 36 int eos; |
2731 | 37 } OGGStreamContext; |
38 | |
3973
549a09cf23fe
Remove offset_t typedef and use int64_t directly instead.
diego
parents:
3593
diff
changeset
|
39 static void ogg_update_checksum(AVFormatContext *s, int64_t crc_offset) |
2731 | 40 { |
3973
549a09cf23fe
Remove offset_t typedef and use int64_t directly instead.
diego
parents:
3593
diff
changeset
|
41 int64_t pos = url_ftell(s->pb); |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2731
diff
changeset
|
42 uint32_t checksum = get_checksum(s->pb); |
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2731
diff
changeset
|
43 url_fseek(s->pb, crc_offset, SEEK_SET); |
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2731
diff
changeset
|
44 put_be32(s->pb, checksum); |
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2731
diff
changeset
|
45 url_fseek(s->pb, pos, SEEK_SET); |
2731 | 46 } |
47 | |
48 static int ogg_write_page(AVFormatContext *s, const uint8_t *data, int size, | |
49 int64_t granule, int stream_index, int flags) | |
50 { | |
51 OGGStreamContext *oggstream = s->streams[stream_index]->priv_data; | |
3973
549a09cf23fe
Remove offset_t typedef and use int64_t directly instead.
diego
parents:
3593
diff
changeset
|
52 int64_t crc_offset; |
2731 | 53 int page_segments, i; |
54 | |
3020
38777f77320e
it seems ogg requires granule to be -1 on unfinished packets
bcoudurier
parents:
2771
diff
changeset
|
55 if (size >= 255*255) { |
38777f77320e
it seems ogg requires granule to be -1 on unfinished packets
bcoudurier
parents:
2771
diff
changeset
|
56 granule = -1; |
38777f77320e
it seems ogg requires granule to be -1 on unfinished packets
bcoudurier
parents:
2771
diff
changeset
|
57 size = 255*255; |
3021 | 58 } else if (oggstream->eos) |
59 flags |= 4; | |
3020
38777f77320e
it seems ogg requires granule to be -1 on unfinished packets
bcoudurier
parents:
2771
diff
changeset
|
60 |
2731 | 61 page_segments = FFMIN((size/255)+!!size, 255); |
62 | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2731
diff
changeset
|
63 init_checksum(s->pb, ff_crc04C11DB7_update, 0); |
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2731
diff
changeset
|
64 put_tag(s->pb, "OggS"); |
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2731
diff
changeset
|
65 put_byte(s->pb, 0); |
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2731
diff
changeset
|
66 put_byte(s->pb, flags); |
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2731
diff
changeset
|
67 put_le64(s->pb, granule); |
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2731
diff
changeset
|
68 put_le32(s->pb, stream_index); |
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2731
diff
changeset
|
69 put_le32(s->pb, oggstream->page_counter++); |
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2731
diff
changeset
|
70 crc_offset = url_ftell(s->pb); |
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2731
diff
changeset
|
71 put_le32(s->pb, 0); // crc |
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2731
diff
changeset
|
72 put_byte(s->pb, page_segments); |
2731 | 73 for (i = 0; i < page_segments-1; i++) |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2731
diff
changeset
|
74 put_byte(s->pb, 255); |
2731 | 75 if (size) { |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2731
diff
changeset
|
76 put_byte(s->pb, size - (page_segments-1)*255); |
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2731
diff
changeset
|
77 put_buffer(s->pb, data, size); |
2731 | 78 } |
79 ogg_update_checksum(s, crc_offset); | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2731
diff
changeset
|
80 put_flush_packet(s->pb); |
2731 | 81 return size; |
82 } | |
83 | |
84 static int ogg_build_flac_headers(const uint8_t *extradata, int extradata_size, | |
85 OGGStreamContext *oggstream, int bitexact) | |
86 { | |
87 const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT; | |
88 uint8_t *p; | |
89 if (extradata_size != 34) | |
90 return -1; | |
3188 | 91 oggstream->header_len[0] = 51; |
92 oggstream->header[0] = av_mallocz(51); // per ogg flac specs | |
2731 | 93 p = oggstream->header[0]; |
94 bytestream_put_byte(&p, 0x7F); | |
95 bytestream_put_buffer(&p, "FLAC", 4); | |
96 bytestream_put_byte(&p, 1); // major version | |
97 bytestream_put_byte(&p, 0); // minor version | |
98 bytestream_put_be16(&p, 1); // headers packets without this one | |
99 bytestream_put_buffer(&p, "fLaC", 4); | |
100 bytestream_put_byte(&p, 0x00); // streaminfo | |
101 bytestream_put_be24(&p, 34); | |
102 bytestream_put_buffer(&p, extradata, 34); | |
103 oggstream->header_len[1] = 1+3+4+strlen(vendor)+4; | |
104 oggstream->header[1] = av_mallocz(oggstream->header_len[1]); | |
105 p = oggstream->header[1]; | |
106 bytestream_put_byte(&p, 0x84); // last metadata block and vorbis comment | |
107 bytestream_put_be24(&p, oggstream->header_len[1] - 4); | |
108 bytestream_put_le32(&p, strlen(vendor)); | |
109 bytestream_put_buffer(&p, vendor, strlen(vendor)); | |
110 bytestream_put_le32(&p, 0); // user comment list length | |
111 return 0; | |
112 } | |
113 | |
114 static int ogg_write_header(AVFormatContext *s) | |
115 { | |
116 OGGStreamContext *oggstream; | |
117 int i, j; | |
118 for (i = 0; i < s->nb_streams; i++) { | |
119 AVStream *st = s->streams[i]; | |
120 if (st->codec->codec_type == CODEC_TYPE_AUDIO) | |
121 av_set_pts_info(st, 64, 1, st->codec->sample_rate); | |
122 else if (st->codec->codec_type == CODEC_TYPE_VIDEO) | |
123 av_set_pts_info(st, 64, st->codec->time_base.num, st->codec->time_base.den); | |
124 if (st->codec->codec_id != CODEC_ID_VORBIS && | |
125 st->codec->codec_id != CODEC_ID_THEORA && | |
126 st->codec->codec_id != CODEC_ID_FLAC) { | |
127 av_log(s, AV_LOG_ERROR, "Unsupported codec id in stream %d\n", i); | |
128 return -1; | |
129 } | |
130 | |
131 if (!st->codec->extradata || !st->codec->extradata_size) { | |
132 av_log(s, AV_LOG_ERROR, "No extradata present\n"); | |
133 return -1; | |
134 } | |
135 oggstream = av_mallocz(sizeof(*oggstream)); | |
136 st->priv_data = oggstream; | |
137 if (st->codec->codec_id == CODEC_ID_FLAC) { | |
138 if (ogg_build_flac_headers(st->codec->extradata, st->codec->extradata_size, | |
139 oggstream, st->codec->flags & CODEC_FLAG_BITEXACT) < 0) { | |
140 av_log(s, AV_LOG_ERROR, "Extradata corrupted\n"); | |
141 av_freep(&st->priv_data); | |
142 } | |
143 } else { | |
144 if (ff_split_xiph_headers(st->codec->extradata, st->codec->extradata_size, | |
145 st->codec->codec_id == CODEC_ID_VORBIS ? 30 : 42, | |
146 oggstream->header, oggstream->header_len) < 0) { | |
147 av_log(s, AV_LOG_ERROR, "Extradata corrupted\n"); | |
148 av_freep(&st->priv_data); | |
149 return -1; | |
150 } | |
151 if (st->codec->codec_id == CODEC_ID_THEORA) { | |
152 /** KFGSHIFT is the width of the less significant section of the granule position | |
153 The less significant section is the frame count since the last keyframe */ | |
154 oggstream->kfgshift = ((oggstream->header[0][40]&3)<<3)|(oggstream->header[0][41]>>5); | |
155 oggstream->vrev = oggstream->header[0][9]; | |
156 av_log(s, AV_LOG_DEBUG, "theora kfgshift %d, vrev %d\n", | |
157 oggstream->kfgshift, oggstream->vrev); | |
158 } | |
159 } | |
160 } | |
161 for (i = 0; i < 3; i++) { | |
162 for (j = 0; j < s->nb_streams; j++) { | |
163 AVStream *st = s->streams[j]; | |
164 OGGStreamContext *oggstream = st->priv_data; | |
165 if (oggstream && oggstream->header_len[i]) { | |
166 ogg_write_page(s, oggstream->header[i], oggstream->header_len[i], | |
167 0, st->index, i ? 0 : 2); // bos | |
168 } | |
169 } | |
170 } | |
171 return 0; | |
172 } | |
173 | |
174 static int ogg_write_packet(AVFormatContext *s, AVPacket *pkt) | |
175 { | |
176 AVStream *st = s->streams[pkt->stream_index]; | |
177 OGGStreamContext *oggstream = st->priv_data; | |
178 uint8_t *ptr = pkt->data; | |
179 int ret, size = pkt->size; | |
180 int64_t granule; | |
181 | |
182 if (st->codec->codec_id == CODEC_ID_THEORA) { | |
183 int64_t pts = oggstream->vrev < 1 ? pkt->pts : pkt->pts + pkt->duration; | |
184 int pframe_count; | |
185 if (pkt->flags & PKT_FLAG_KEY) | |
186 oggstream->last_kf_pts = pts; | |
187 pframe_count = pts - oggstream->last_kf_pts; | |
188 // prevent frame count from overflow if key frame flag is not set | |
189 if (pframe_count >= (1<<oggstream->kfgshift)) { | |
190 oggstream->last_kf_pts += pframe_count; | |
191 pframe_count = 0; | |
192 } | |
193 granule = (oggstream->last_kf_pts<<oggstream->kfgshift) | pframe_count; | |
194 } else | |
195 granule = pkt->pts + pkt->duration; | |
196 oggstream->duration = granule; | |
197 do { | |
198 ret = ogg_write_page(s, ptr, size, granule, pkt->stream_index, ptr != pkt->data); | |
199 ptr += ret; size -= ret; | |
200 } while (size > 0 || ret == 255*255); // need to output a last nil page | |
201 | |
202 return 0; | |
203 } | |
204 | |
3021 | 205 int ogg_interleave_per_granule(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush) |
206 { | |
207 AVPacketList *pktl, **next_point, *this_pktl; | |
208 int stream_count = 0; | |
209 int streams[MAX_STREAMS] = {0}; | |
210 int interleaved = 0; | |
211 | |
212 if (pkt) { | |
213 AVStream *st = s->streams[pkt->stream_index]; | |
214 this_pktl = av_mallocz(sizeof(AVPacketList)); | |
215 this_pktl->pkt = *pkt; | |
216 if (pkt->destruct == av_destruct_packet) | |
217 pkt->destruct = NULL; // not shared -> must keep original from being freed | |
218 else | |
219 av_dup_packet(&this_pktl->pkt); // shared -> must dup | |
220 next_point = &s->packet_buffer; | |
221 while (*next_point) { | |
222 AVStream *st2 = s->streams[(*next_point)->pkt.stream_index]; | |
223 AVPacket *next_pkt = &(*next_point)->pkt; | |
224 int64_t cur_granule, next_granule; | |
225 next_granule = av_rescale_q(next_pkt->pts + next_pkt->duration, | |
226 st2->time_base, AV_TIME_BASE_Q); | |
227 cur_granule = av_rescale_q(pkt->pts + pkt->duration, | |
3592 | 228 st->time_base, AV_TIME_BASE_Q); |
3021 | 229 if (next_granule > cur_granule) |
230 break; | |
231 next_point= &(*next_point)->next; | |
232 } | |
233 this_pktl->next= *next_point; | |
234 *next_point= this_pktl; | |
235 } | |
236 | |
237 pktl = s->packet_buffer; | |
238 while (pktl) { | |
239 if (streams[pktl->pkt.stream_index] == 0) | |
240 stream_count++; | |
241 streams[pktl->pkt.stream_index]++; | |
242 // need to buffer at least one packet to set eos flag | |
243 if (streams[pktl->pkt.stream_index] == 2) | |
244 interleaved++; | |
245 pktl = pktl->next; | |
246 } | |
247 | |
248 if ((s->nb_streams == stream_count && interleaved == stream_count) || | |
249 (flush && stream_count)) { | |
250 pktl= s->packet_buffer; | |
251 *out= pktl->pkt; | |
252 s->packet_buffer = pktl->next; | |
253 if (flush && streams[out->stream_index] == 1) { | |
254 OGGStreamContext *ogg = s->streams[out->stream_index]->priv_data; | |
255 ogg->eos = 1; | |
256 } | |
257 av_freep(&pktl); | |
258 return 1; | |
259 } else { | |
260 av_init_packet(out); | |
261 return 0; | |
262 } | |
263 } | |
2731 | 264 |
265 static int ogg_write_trailer(AVFormatContext *s) | |
266 { | |
267 int i; | |
268 for (i = 0; i < s->nb_streams; i++) { | |
269 AVStream *st = s->streams[i]; | |
270 OGGStreamContext *oggstream = st->priv_data; | |
271 if (st->codec->codec_id == CODEC_ID_FLAC) { | |
272 av_free(oggstream->header[0]); | |
273 av_free(oggstream->header[1]); | |
274 } | |
275 av_freep(&st->priv_data); | |
276 } | |
277 return 0; | |
278 } | |
279 | |
280 AVOutputFormat ogg_muxer = { | |
281 "ogg", | |
3424
7a0230981402
Make long_names in lavf/lavdev optional depending on CONFIG_SMALL.
diego
parents:
3286
diff
changeset
|
282 NULL_IF_CONFIG_SMALL("Ogg"), |
2731 | 283 "application/ogg", |
3593 | 284 "ogg,ogv", |
2731 | 285 0, |
286 CODEC_ID_FLAC, | |
287 CODEC_ID_THEORA, | |
288 ogg_write_header, | |
289 ogg_write_packet, | |
290 ogg_write_trailer, | |
3021 | 291 .interleave_packet = ogg_interleave_per_granule, |
2731 | 292 }; |