0
|
1 /*
|
|
2 * Ogg bitstream support
|
|
3 * Mark Hills <mark@pogo.org.uk>
|
|
4 *
|
|
5 * Uses libogg, but requires libvorbisenc to construct correct headers
|
|
6 * when containing Vorbis stream -- currently the only format supported
|
|
7 */
|
|
8
|
|
9 #include <stdio.h>
|
|
10 #include <time.h>
|
|
11
|
|
12 #include <ogg/ogg.h>
|
|
13 #include <vorbis/vorbisenc.h>
|
|
14
|
|
15 #include "avformat.h"
|
|
16 #include "oggvorbis.h"
|
|
17
|
|
18 #define DECODER_BUFFER_SIZE 4096
|
|
19
|
|
20
|
|
21 typedef struct OggContext {
|
|
22 /* output */
|
|
23 ogg_stream_state os ;
|
|
24 int header_handled ;
|
|
25 ogg_int64_t base_packet_no ;
|
|
26 ogg_int64_t base_granule_pos ;
|
|
27
|
|
28 /* input */
|
|
29 ogg_sync_state oy ;
|
|
30 } OggContext ;
|
|
31
|
|
32
|
35
|
33 static int ogg_write_header(AVFormatContext *avfcontext)
|
|
34 {
|
|
35 OggContext *context = avfcontext->priv_data;
|
0
|
36 AVCodecContext *avccontext ;
|
|
37 vorbis_info vi ;
|
|
38 vorbis_dsp_state vd ;
|
|
39 vorbis_comment vc ;
|
|
40 vorbis_block vb ;
|
|
41 ogg_packet header, header_comm, header_code ;
|
|
42 int n ;
|
|
43
|
|
44 srand(time(NULL));
|
|
45 ogg_stream_init(&context->os, rand());
|
|
46
|
|
47 for(n = 0 ; n < avfcontext->nb_streams ; n++) {
|
|
48 avccontext = &avfcontext->streams[n]->codec ;
|
|
49
|
|
50 /* begin vorbis specific code */
|
|
51
|
|
52 vorbis_info_init(&vi) ;
|
|
53
|
|
54 /* code copied from libavcodec/oggvorbis.c */
|
|
55
|
|
56 if(oggvorbis_init_encoder(&vi, avccontext) < 0) {
|
|
57 fprintf(stderr, "ogg_write_header: init_encoder failed") ;
|
|
58 return -1 ;
|
|
59 }
|
|
60
|
|
61 vorbis_analysis_init(&vd, &vi) ;
|
|
62 vorbis_block_init(&vd, &vb) ;
|
|
63
|
|
64 vorbis_comment_init(&vc) ;
|
|
65 vorbis_comment_add_tag(&vc, "encoder", "ffmpeg") ;
|
|
66 if(*avfcontext->title)
|
|
67 vorbis_comment_add_tag(&vc, "title", avfcontext->title) ;
|
|
68
|
|
69 vorbis_analysis_headerout(&vd, &vc, &header,
|
|
70 &header_comm, &header_code) ;
|
|
71 ogg_stream_packetin(&context->os, &header) ;
|
|
72 ogg_stream_packetin(&context->os, &header_comm) ;
|
|
73 ogg_stream_packetin(&context->os, &header_code) ;
|
|
74
|
|
75 vorbis_comment_clear(&vc) ;
|
|
76
|
|
77 /* end of vorbis specific code */
|
|
78
|
|
79 context->header_handled = 0 ;
|
|
80 context->base_packet_no = 0 ;
|
|
81 }
|
|
82
|
|
83 return 0 ;
|
|
84 }
|
|
85
|
|
86
|
|
87 static int ogg_write_packet(AVFormatContext *avfcontext,
|
|
88 int stream_index,
|
|
89 unsigned char *buf, int size, int force_pts)
|
|
90 {
|
|
91 OggContext *context = avfcontext->priv_data ;
|
|
92 ogg_packet *op ;
|
|
93 ogg_page og ;
|
|
94 int l = 0 ;
|
|
95
|
|
96 /* flush header packets so audio starts on a new page */
|
|
97
|
|
98 if(!context->header_handled) {
|
|
99 while(ogg_stream_flush(&context->os, &og)) {
|
|
100 put_buffer(&avfcontext->pb, og.header, og.header_len) ;
|
|
101 put_buffer(&avfcontext->pb, og.body, og.body_len) ;
|
|
102 put_flush_packet(&avfcontext->pb);
|
|
103 }
|
|
104 context->header_handled = 1 ;
|
|
105 }
|
|
106
|
|
107 while(l < size) {
|
|
108 op = (ogg_packet*)(buf + l) ;
|
|
109 op->packet = buf + l + sizeof(ogg_packet) ; /* fix data pointer */
|
|
110
|
|
111 if(!context->base_packet_no) { /* this is the first packet */
|
|
112 context->base_packet_no = op->packetno ;
|
|
113 context->base_granule_pos = op->granulepos ;
|
|
114 }
|
|
115
|
|
116 /* correct the fields in the packet -- essential for streaming */
|
|
117
|
|
118 op->packetno -= context->base_packet_no ;
|
|
119 op->granulepos -= context->base_granule_pos ;
|
|
120
|
|
121 ogg_stream_packetin(&context->os, op) ;
|
|
122 l += sizeof(ogg_packet) + op->bytes ;
|
|
123
|
|
124 while(ogg_stream_pageout(&context->os, &og)) {
|
|
125 put_buffer(&avfcontext->pb, og.header, og.header_len) ;
|
|
126 put_buffer(&avfcontext->pb, og.body, og.body_len) ;
|
|
127 put_flush_packet(&avfcontext->pb);
|
|
128 }
|
|
129 }
|
|
130
|
|
131 return 0;
|
|
132 }
|
|
133
|
|
134
|
|
135 static int ogg_write_trailer(AVFormatContext *avfcontext) {
|
|
136 OggContext *context = avfcontext->priv_data ;
|
|
137 ogg_page og ;
|
|
138
|
|
139 while(ogg_stream_flush(&context->os, &og)) {
|
|
140 put_buffer(&avfcontext->pb, og.header, og.header_len) ;
|
|
141 put_buffer(&avfcontext->pb, og.body, og.body_len) ;
|
|
142 put_flush_packet(&avfcontext->pb);
|
|
143 }
|
|
144
|
|
145 ogg_stream_clear(&context->os) ;
|
|
146 return 0 ;
|
|
147 }
|
|
148
|
|
149
|
|
150 static AVOutputFormat ogg_oformat = {
|
|
151 "ogg",
|
|
152 "Ogg Vorbis",
|
|
153 "audio/x-vorbis",
|
|
154 "ogg",
|
|
155 sizeof(OggContext),
|
|
156 CODEC_ID_VORBIS,
|
|
157 0,
|
|
158 ogg_write_header,
|
|
159 ogg_write_packet,
|
|
160 ogg_write_trailer,
|
|
161 } ;
|
|
162
|
|
163
|
|
164 static int next_packet(AVFormatContext *avfcontext, ogg_packet *op) {
|
|
165 OggContext *context = avfcontext->priv_data ;
|
|
166 ogg_page og ;
|
|
167 char *buf ;
|
|
168
|
|
169 while(ogg_stream_packetout(&context->os, op) != 1) {
|
|
170
|
|
171 /* while no pages are available, read in more data to the sync */
|
|
172 while(ogg_sync_pageout(&context->oy, &og) != 1) {
|
|
173 buf = ogg_sync_buffer(&context->oy, DECODER_BUFFER_SIZE) ;
|
|
174 if(get_buffer(&avfcontext->pb, buf, DECODER_BUFFER_SIZE) <= 0)
|
|
175 return 1 ;
|
|
176 ogg_sync_wrote(&context->oy, DECODER_BUFFER_SIZE) ;
|
|
177 }
|
|
178
|
|
179 /* got a page. Feed it into the stream and get the packet */
|
|
180 if(ogg_stream_pagein(&context->os, &og) != 0)
|
|
181 return 1 ;
|
|
182 }
|
|
183
|
|
184 return 0 ;
|
|
185 }
|
|
186
|
|
187
|
|
188 static int ogg_read_header(AVFormatContext *avfcontext, AVFormatParameters *ap)
|
|
189 {
|
35
|
190 OggContext *context = avfcontext->priv_data;
|
0
|
191 char *buf ;
|
|
192 ogg_page og ;
|
|
193 AVStream *ast ;
|
|
194
|
|
195 ogg_sync_init(&context->oy) ;
|
|
196 buf = ogg_sync_buffer(&context->oy, DECODER_BUFFER_SIZE) ;
|
|
197
|
|
198 if(get_buffer(&avfcontext->pb, buf, DECODER_BUFFER_SIZE) <= 0)
|
|
199 return -EIO ;
|
|
200
|
|
201 ogg_sync_wrote(&context->oy, DECODER_BUFFER_SIZE) ;
|
|
202 ogg_sync_pageout(&context->oy, &og) ;
|
|
203 ogg_stream_init(&context->os, ogg_page_serialno(&og)) ;
|
|
204 ogg_stream_pagein(&context->os, &og) ;
|
|
205
|
|
206 /* currently only one vorbis stream supported */
|
|
207
|
|
208 ast = av_new_stream(avfcontext, 0) ;
|
|
209 if(!ast)
|
|
210 return AVERROR_NOMEM ;
|
|
211
|
|
212 ast->codec.codec_type = CODEC_TYPE_AUDIO ;
|
|
213 ast->codec.codec_id = CODEC_ID_VORBIS ;
|
|
214
|
|
215 return 0 ;
|
|
216 }
|
|
217
|
|
218
|
|
219 static int ogg_read_packet(AVFormatContext *avfcontext, AVPacket *pkt) {
|
|
220 ogg_packet op ;
|
|
221
|
|
222 if(next_packet(avfcontext, &op))
|
|
223 return -EIO ;
|
|
224 if(av_new_packet(pkt, sizeof(ogg_packet) + op.bytes) < 0)
|
|
225 return -EIO ;
|
|
226 pkt->stream_index = 0 ;
|
|
227 memcpy(pkt->data, &op, sizeof(ogg_packet)) ;
|
|
228 memcpy(pkt->data + sizeof(ogg_packet), op.packet, op.bytes) ;
|
|
229
|
|
230 return sizeof(ogg_packet) + op.bytes ;
|
|
231 }
|
|
232
|
|
233
|
|
234 static int ogg_read_close(AVFormatContext *avfcontext) {
|
|
235 OggContext *context = avfcontext->priv_data ;
|
|
236
|
|
237 ogg_stream_clear(&context->os) ;
|
|
238 ogg_sync_clear(&context->oy) ;
|
|
239
|
|
240 return 0 ;
|
|
241 }
|
|
242
|
|
243
|
|
244 static AVInputFormat ogg_iformat = {
|
|
245 "ogg",
|
|
246 "Ogg Vorbis",
|
|
247 sizeof(OggContext),
|
|
248 NULL,
|
|
249 ogg_read_header,
|
|
250 ogg_read_packet,
|
|
251 ogg_read_close,
|
|
252 .extensions = "ogg",
|
|
253 } ;
|
|
254
|
|
255
|
|
256 int ogg_init(void) {
|
|
257 av_register_output_format(&ogg_oformat) ;
|
|
258 av_register_input_format(&ogg_iformat);
|
|
259 return 0 ;
|
|
260 }
|