1 /* -*- tab-width:4;c-file-style:"cc-mode"; -*- */
3 * theorautils.c - Ogg Theora/Ogg Vorbis Abstraction and Muxing
4 * Copyright (C) 2003-2005 <j@v2v.cc>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 #include "theora/theora.h"
29 #include "vorbis/codec.h"
30 #include "vorbis/vorbisenc.h"
32 #include "theorautils.h"
36 static double rint(double x
)
39 return (double)(int)(x
- 0.5);
41 return (double)(int)(x
+ 0.5);
44 void init_info(oggmux_info
*info
) {
45 info
->with_skeleton
= 0; /* skeleton is disabled by default */
46 info
->frontend
= 0; /*frontend mode*/
49 info
->audio_bytesout
= 0;
50 info
->video_bytesout
= 0;
52 info
->videopage_valid
= 0;
53 info
->audiopage_valid
= 0;
54 info
->audiopage_buffer_length
= 0;
55 info
->videopage_buffer_length
= 0;
56 info
->audiopage
= NULL
;
57 info
->videopage
= NULL
;
58 info
->start_time
= time(NULL
);
60 info
->speed_level
= -1;
70 void add_fishead_packet (oggmux_info
*info
) {
73 memset (&op
, 0, sizeof (op
));
75 op
.packet
= _ogg_calloc (64, sizeof(unsigned char));
76 memset (op
.packet
, 0, 64);
77 memcpy (op
.packet
, FISHEAD_IDENTIFIER
, 8); /* identifier */
78 *((ogg_uint16_t
*)(op
.packet
+8)) = SKELETON_VERSION_MAJOR
; /* version major */
79 *((ogg_uint16_t
*)(op
.packet
+10)) = SKELETON_VERSION_MINOR
; /* version minor */
80 *((ogg_int64_t
*)(op
.packet
+12)) = (ogg_int64_t
)0; /* presentationtime numerator */
81 *((ogg_int64_t
*)(op
.packet
+20)) = (ogg_int64_t
)1000; /* presentationtime denominator */
82 *((ogg_int64_t
*)(op
.packet
+28)) = (ogg_int64_t
)0; /* basetime numerator */
83 *((ogg_int64_t
*)(op
.packet
+36)) = (ogg_int64_t
)1000; /* basetime denominator */
84 /* both the numerator are zero hence handled by the memset */
85 *((ogg_uint32_t
*)(op
.packet
+44)) = 0; /* UTC time, set to zero for now */
87 op
.b_o_s
= 1; /* its the first packet of the stream */
88 op
.e_o_s
= 0; /* its not the last packet of the stream */
89 op
.bytes
= 64; /* length of the packet in bytes */
91 ogg_stream_packetin (&info
->so
, &op
); /* adding the packet to the skeleton stream */
92 _ogg_free (op
.packet
);
96 * Adds the fishead packets in the skeleton output stream along with the e_o_s packet
98 void add_fisbone_packet (oggmux_info
*info
) {
101 if (!info
->audio_only
) {
102 memset (&op
, 0, sizeof (op
));
103 op
.packet
= _ogg_calloc (82, sizeof(unsigned char));
104 memset (op
.packet
, 0, 82);
105 /* it will be the fisbone packet for the theora video */
106 memcpy (op
.packet
, FISBONE_IDENTIFIER
, 8); /* identifier */
107 *((ogg_uint32_t
*)(op
.packet
+8)) = FISBONE_MESSAGE_HEADER_OFFSET
; /* offset of the message header fields */
108 *((ogg_uint32_t
*)(op
.packet
+12)) = info
->to
.serialno
; /* serialno of the theora stream */
109 *((ogg_uint32_t
*)(op
.packet
+16)) = 3; /* number of header packets */
110 /* granulerate, temporal resolution of the bitstream in samples/microsecond */
111 *((ogg_int64_t
*)(op
.packet
+20)) = info
->ti
.fps_numerator
; /* granulrate numerator */
112 *((ogg_int64_t
*)(op
.packet
+28)) = info
->ti
.fps_denominator
; /* granulrate denominator */
113 *((ogg_int64_t
*)(op
.packet
+36)) = 0; /* start granule */
114 *((ogg_uint32_t
*)(op
.packet
+44)) = 0; /* preroll, for theora its 0 */
115 *(op
.packet
+48) = theora_granule_shift (&info
->ti
); /* granule shift */
116 memcpy(op
.packet
+FISBONE_SIZE
, "Content-Type: video/x-theora\r\n", 30); /* message header field, Content-Type */
120 op
.bytes
= 82; /* size of the packet in bytes */
122 ogg_stream_packetin (&info
->so
, &op
);
123 _ogg_free (op
.packet
);
126 if (!info
->video_only
) {
127 memset (&op
, 0, sizeof (op
));
128 op
.packet
= _ogg_calloc (82, sizeof(unsigned char));
129 memset (op
.packet
, 0, 82);
130 /* it will be the fisbone packet for the vorbis audio */
131 memcpy (op
.packet
, FISBONE_IDENTIFIER
, 8); /* identifier */
132 *((ogg_uint32_t
*)(op
.packet
+8)) = FISBONE_MESSAGE_HEADER_OFFSET
; /* offset of the message header fields */
133 *((ogg_uint32_t
*)(op
.packet
+12)) = info
->vo
.serialno
; /* serialno of the vorbis stream */
134 *((ogg_uint32_t
*)(op
.packet
+16)) = 3; /* number of header packet */
135 /* granulerate, temporal resolution of the bitstream in Hz */
136 *((ogg_int64_t
*)(op
.packet
+20)) = info
->sample_rate
; /* granulerate numerator */
137 *((ogg_int64_t
*)(op
.packet
+28)) = (ogg_int64_t
)1; /* granulerate denominator */
138 *((ogg_int64_t
*)(op
.packet
+36)) = 0; /* start granule */
139 *((ogg_uint32_t
*)(op
.packet
+44)) = 2; /* preroll, for vorbis its 2 */
140 *(op
.packet
+48) = 0; /* granule shift, always 0 for vorbis */
141 memcpy (op
.packet
+FISBONE_SIZE
, "Content-Type: audio/x-vorbis\r\n", 30);
142 /* Important: Check the case of Content-Type for correctness */
148 ogg_stream_packetin (&info
->so
, &op
);
149 _ogg_free (op
.packet
);
153 void oggmux_init (oggmux_info
*info
){
157 /* yayness. Set up Ogg output stream */
159 ogg_stream_init (&info
->vo
, rand ());
161 if(!info
->audio_only
){
162 ogg_stream_init (&info
->to
, rand ()); /* oops, add one ot the above */
163 theora_encode_init (&info
->td
, &info
->ti
);
165 if(info
->speed_level
>= 0) {
167 theora_control(&info
->td
, TH_ENCCTL_GET_SPLEVEL_MAX
, &max_speed_level
, sizeof(int));
168 if(info
->speed_level
> max_speed_level
)
169 info
->speed_level
= max_speed_level
;
170 theora_control(&info
->td
, TH_ENCCTL_SET_SPLEVEL
, &info
->speed_level
, sizeof(int));
173 /* init theora done */
175 /* initialize Vorbis too, if we have audio. */
176 if(!info
->video_only
){
178 vorbis_info_init (&info
->vi
);
179 /* Encoding using a VBR quality mode. */
180 if(info
->vorbis_quality
>-99)
181 ret
=vorbis_encode_init_vbr (&info
->vi
, info
->channels
,info
->sample_rate
,info
->vorbis_quality
);
183 ret
=vorbis_encode_init(&info
->vi
,info
->channels
,info
->sample_rate
,-1,info
->vorbis_bitrate
,-1);
187 "The Vorbis encoder could not set up a mode according to\n"
188 "the requested quality or bitrate.\n\n");
192 vorbis_comment_init (&info
->vc
);
193 vorbis_comment_add_tag (&info
->vc
, "ENCODER",PACKAGE_STRING
);
194 /* set up the analysis state and auxiliary encoding storage */
195 vorbis_analysis_init (&info
->vd
, &info
->vi
);
196 vorbis_block_init (&info
->vd
, &info
->vb
);
199 /* audio init done */
201 /* first packet should be skeleton fishead packet, if skeleton is used */
203 if (info
->with_skeleton
) {
204 ogg_stream_init (&info
->so
, rand());
205 add_fishead_packet (info
);
206 if (ogg_stream_pageout (&info
->so
, &og
) != 1){
207 fprintf (stderr
, "Internal Ogg library error.\n");
210 fwrite (og
.header
, 1, og
.header_len
, info
->outfile
);
211 fwrite (og
.body
, 1, og
.body_len
, info
->outfile
);
214 /* write the bitstream header packets with proper page interleave */
216 /* first packet will get its own page automatically */
217 if(!info
->audio_only
){
218 theora_encode_header (&info
->td
, &op
);
219 ogg_stream_packetin (&info
->to
, &op
);
220 if (ogg_stream_pageout (&info
->to
, &og
) != 1){
221 fprintf (stderr
, "Internal Ogg library error.\n");
224 fwrite (og
.header
, 1, og
.header_len
, info
->outfile
);
225 fwrite (og
.body
, 1, og
.body_len
, info
->outfile
);
227 /* create the remaining theora headers */
228 /* theora_comment_init (&info->tc); is called in main() prior to parsing options */
229 theora_comment_add_tag (&info
->tc
, "ENCODER",PACKAGE_STRING
);
230 theora_encode_comment (&info
->tc
, &op
);
231 ogg_stream_packetin (&info
->to
, &op
);
232 theora_encode_tables (&info
->td
, &op
);
233 ogg_stream_packetin (&info
->to
, &op
);
235 if(!info
->video_only
){
237 ogg_packet header_comm
;
238 ogg_packet header_code
;
240 vorbis_analysis_headerout (&info
->vd
, &info
->vc
, &header
,
241 &header_comm
, &header_code
);
242 ogg_stream_packetin (&info
->vo
, &header
); /* automatically placed in its own
244 if (ogg_stream_pageout (&info
->vo
, &og
) != 1){
245 fprintf (stderr
, "Internal Ogg library error.\n");
248 fwrite (og
.header
, 1, og
.header_len
, info
->outfile
);
249 fwrite (og
.body
, 1, og
.body_len
, info
->outfile
);
251 /* remaining vorbis header packets */
252 ogg_stream_packetin (&info
->vo
, &header_comm
);
253 ogg_stream_packetin (&info
->vo
, &header_code
);
256 /* output the appropriate fisbone packets */
257 if (info
->with_skeleton
) {
258 add_fisbone_packet (info
);
260 int result
= ogg_stream_flush (&info
->so
, &og
);
263 fprintf (stderr
, "Internal Ogg library error.\n");
268 fwrite (og
.header
, 1, og
.header_len
, info
->outfile
);
269 fwrite (og
.body
, 1, og
.body_len
, info
->outfile
);
273 if (!info
->audio_only
) {
274 theora_info_clear(&info
->ti
);
277 /* Flush the rest of our headers. This ensures
278 * the actual data in each stream will start
279 * on a new page, as per spec. */
280 while (1 && !info
->audio_only
){
281 int result
= ogg_stream_flush (&info
->to
, &og
);
284 fprintf (stderr
, "Internal Ogg library error.\n");
289 fwrite (og
.header
, 1, og
.header_len
, info
->outfile
);
290 fwrite (og
.body
, 1, og
.body_len
, info
->outfile
);
292 while (1 && !info
->video_only
){
293 int result
= ogg_stream_flush (&info
->vo
, &og
);
296 fprintf (stderr
, "Internal Ogg library error.\n");
301 fwrite (og
.header
, 1, og
.header_len
,info
->outfile
);
302 fwrite (og
.body
, 1, og
.body_len
, info
->outfile
);
305 if (info
->with_skeleton
) {
308 /* build and add the e_o_s packet */
309 memset (&op
, 0, sizeof (op
));
311 op
.e_o_s
= 1; /* its the e_o_s packet */
313 op
.bytes
= 0; /* e_o_s packet is an empty packet */
314 ogg_stream_packetin (&info
->so
, &op
);
316 result
= ogg_stream_flush (&info
->so
, &og
);
319 fprintf (stderr
, "Internal Ogg library error.\n");
322 fwrite (og
.header
, 1, og
.header_len
,info
->outfile
);
323 fwrite (og
.body
, 1, og
.body_len
, info
->outfile
);
328 * adds a video frame to the encoding sink
329 * if e_o_s is 1 the end of the logical bitstream will be marked.
330 * @param this ff2theora struct
331 * @param info oggmux_info
333 * @param e_o_s 1 indicates ond of stream
335 void oggmux_add_video (oggmux_info
*info
, yuv_buffer
*yuv
, int e_o_s
){
337 theora_encode_YUVin (&info
->td
, yuv
);
338 while(theora_encode_packetout (&info
->td
, e_o_s
, &op
)) {
339 ogg_stream_packetin (&info
->to
, &op
);
345 * adds audio samples to encoding sink
346 * @param buffer pointer to buffer
347 * @param bytes bytes in buffer
348 * @param samples samples in buffer
349 * @param e_o_s 1 indicates end of stream.
351 void oggmux_add_audio (oggmux_info
*info
, int16_t * buffer
, int bytes
, int samples
, int e_o_s
){
355 float **vorbis_buffer
;
356 if (bytes
<= 0 && samples
<= 0){
357 /* end of audio stream */
359 vorbis_analysis_wrote (&info
->vd
, 0);
362 vorbis_buffer
= vorbis_analysis_buffer (&info
->vd
, samples
);
363 /* uninterleave samples */
364 for (i
= 0; i
< samples
; i
++){
365 for(j
=0;j
<info
->channels
;j
++){
366 vorbis_buffer
[j
][i
] = buffer
[count
++] / 32768.f
;
369 vorbis_analysis_wrote (&info
->vd
, samples
);
371 while(vorbis_analysis_blockout (&info
->vd
, &info
->vb
) == 1){
372 /* analysis, assume we want to use bitrate management */
373 vorbis_analysis (&info
->vb
, NULL
);
374 vorbis_bitrate_addblock (&info
->vb
);
376 /* weld packets into the bitstream */
377 while (vorbis_bitrate_flushpacket (&info
->vd
, &op
)){
378 ogg_stream_packetin (&info
->vo
, &op
);
384 static double get_remaining(oggmux_info
*info
, double timebase
) {
385 double remaining
= 0;
386 double to_encode
, time_so_far
;
388 if(info
->duration
!= -1 && timebase
> 0) {
389 time_so_far
= time(NULL
) - info
->start_time
;
390 to_encode
= info
->duration
- timebase
;
392 remaining
= (time_so_far
/ timebase
) * to_encode
;
398 static void print_stats(oggmux_info
*info
, double timebase
){
399 int hundredths
= timebase
* 100 - (long) timebase
* 100;
400 int seconds
= (long) timebase
% 60;
401 int minutes
= ((long) timebase
/ 60) % 60;
402 int hours
= (long) timebase
/ 3600;
403 double remaining
= get_remaining(info
, timebase
);
404 int remaining_seconds
= (long) remaining
% 60;
405 int remaining_minutes
= ((long) remaining
/ 60) % 60;
406 int remaining_hours
= (long) remaining
/ 3600;
408 fprintf (stderr
,"\nf2t ;position: %.02lf;audio_kbps: %d;video_kbps: %d;remaining: %.02lf\n",
410 info
->akbps
, info
->vkbps
,
416 fprintf (stderr
,"\r %d:%02d:%02d.%02d audio: %dkbps video: %dkbps, time remaining: %02d:%02d:%02d ",
417 hours
, minutes
, seconds
, hundredths
,
418 info
->akbps
, info
->vkbps
,
419 remaining_hours
, remaining_minutes
, remaining_seconds
424 static int write_audio_page(oggmux_info
*info
)
428 ret
= fwrite(info
->audiopage
, 1, info
->audiopage_len
, info
->outfile
);
429 if(ret
< info
->audiopage_len
) {
430 fprintf(stderr
,"error writing audio page\n");
433 info
->audio_bytesout
+= ret
;
435 info
->audiopage_valid
= 0;
436 info
->a_pkg
-=ogg_page_packets((ogg_page
*)&info
->audiopage
);
440 fprintf(stderr
,"\naudio page %d (%d pkgs) | pkg remaining %d\n",info
->a_page
,ogg_page_packets((ogg_page
*)&info
->audiopage
),info
->a_pkg
);
443 info
->akbps
= rint (info
->audio_bytesout
* 8. / info
->audiotime
* .001);
446 print_stats(info
, info
->audiotime
);
449 static int write_video_page(oggmux_info
*info
)
453 ret
= fwrite(info
->videopage
, 1, info
->videopage_len
, info
->outfile
);
454 if(ret
< info
->videopage_len
) {
455 fprintf(stderr
,"error writing video page\n");
458 info
->video_bytesout
+= ret
;
460 info
->videopage_valid
= 0;
461 info
->v_pkg
-= ogg_page_packets((ogg_page
*)&info
->videopage
);
465 fprintf(stderr
,"\nvideo page %d (%d pkgs) | pkg remaining %d\n",info
->v_page
,ogg_page_packets((ogg_page
*)&info
->videopage
),info
->v_pkg
);
469 info
->vkbps
= rint (info
->video_bytesout
* 8. / info
->videotime
* .001);
472 print_stats(info
, info
->videotime
);
475 void oggmux_flush (oggmux_info
*info
, int e_o_s
)
480 /* flush out the ogg pages to info->outfile */
482 /* Get pages for both streams, if not already present, and if available.*/
483 if(!info
->audio_only
&& !info
->videopage_valid
) {
484 // this way seeking is much better,
485 // not sure if 23 packets is a good value. it works though
487 if(info
->v_pkg
>22 && ogg_stream_flush(&info
->to
, &og
) > 0) {
490 else if(ogg_stream_pageout(&info
->to
, &og
) > 0) {
494 len
= og
.header_len
+ og
.body_len
;
495 if(info
->videopage_buffer_length
< len
) {
496 info
->videopage
= realloc(info
->videopage
, len
);
497 info
->videopage_buffer_length
= len
;
499 info
->videopage_len
= len
;
500 memcpy(info
->videopage
, og
.header
, og
.header_len
);
501 memcpy(info
->videopage
+og
.header_len
, og
.body
, og
.body_len
);
503 info
->videopage_valid
= 1;
504 if(ogg_page_granulepos(&og
)>0) {
505 info
->videotime
= theora_granule_time (&info
->td
,
506 ogg_page_granulepos(&og
));
510 if(!info
->video_only
&& !info
->audiopage_valid
) {
511 // this way seeking is much better,
512 // not sure if 23 packets is a good value. it works though
514 if(info
->a_pkg
>22 && ogg_stream_flush(&info
->vo
, &og
) > 0) {
517 else if(ogg_stream_pageout(&info
->vo
, &og
) > 0) {
521 len
= og
.header_len
+ og
.body_len
;
522 if(info
->audiopage_buffer_length
< len
) {
523 info
->audiopage
= realloc(info
->audiopage
, len
);
524 info
->audiopage_buffer_length
= len
;
526 info
->audiopage_len
= len
;
527 memcpy(info
->audiopage
, og
.header
, og
.header_len
);
528 memcpy(info
->audiopage
+og
.header_len
, og
.body
, og
.body_len
);
530 info
->audiopage_valid
= 1;
531 if(ogg_page_granulepos(&og
)>0) {
532 info
->audiotime
= vorbis_granule_time (&info
->vd
,
533 ogg_page_granulepos(&og
));
538 if(info
->video_only
&& info
->videopage_valid
) {
539 write_video_page(info
);
541 else if(info
->audio_only
&& info
->audiopage_valid
) {
542 write_audio_page(info
);
544 /* We're using both. We can output only:
545 * a) If we have valid pages for both
546 * b) At EOS, for the remaining stream.
548 else if(info
->videopage_valid
&& info
->audiopage_valid
) {
549 /* Make sure they're in the right order. */
550 if(info
->videotime
<= info
->audiotime
)
551 write_video_page(info
);
553 write_audio_page(info
);
555 else if(e_o_s
&& info
->videopage_valid
) {
556 write_video_page(info
);
558 else if(e_o_s
&& info
->audiopage_valid
) {
559 write_audio_page(info
);
562 break; /* Nothing more writable at the moment */
567 void oggmux_close (oggmux_info
*info
){
568 ogg_stream_clear (&info
->vo
);
569 vorbis_block_clear (&info
->vb
);
570 vorbis_dsp_clear (&info
->vd
);
571 vorbis_comment_clear (&info
->vc
);
572 vorbis_info_clear (&info
->vi
);
574 ogg_stream_clear (&info
->to
);
575 theora_clear (&info
->td
);
577 if (info
->outfile
&& info
->outfile
!= stdout
)
578 fclose (info
->outfile
);
581 free(info
->videopage
);
583 free(info
->audiopage
);