1 /* -*- c-basic-offset: 8; -*- */
2 /* theora.c: Ogg Theora data handlers for libshout
3  * $Id$
4  *
5  *  Copyright (C) 2004 the Icecast team <team@icecast.org>
6  *  Copyright (C) 2015-2019 Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Library General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Library General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Library General Public
19  *  License along with this library; if not, write to the Free
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #   include <config.h>
25 #endif
26 
27 #ifdef HAVE_INTTYPES_H
28 #   include <inttypes.h>
29 #endif
30 #include <stdlib.h>
31 
32 #include <theora/theora.h>
33 
34 #include "shout_private.h"
35 #include "format_ogg.h"
36 
37 /* -- local data structures -- */
38 typedef struct {
39     theora_info     ti;
40     theora_comment  tc;
41     uint32_t        granule_shift;
42     double          per_frame;
43     uint64_t        start_frame;
44     int             initial_frames;
45     int             get_start_frame;
46 } theora_data_t;
47 
48 /* -- local prototypes -- */
49 static int  read_theora_page(ogg_codec_t *codec, ogg_page *page);
50 static void free_theora_data(void *codec_data);
51 static int  theora_ilog(unsigned int v);
52 
53 /* -- theora functions -- */
_shout_open_theora(ogg_codec_t * codec,ogg_page * page)54 int _shout_open_theora(ogg_codec_t *codec, ogg_page *page)
55 {
56     ogg_packet  packet;
57     (void)      page;
58 
59     theora_data_t *theora_data = calloc(1, sizeof(theora_data_t));
60 	if (!theora_data)
61         return SHOUTERR_MALLOC;
62 
63     theora_info_init(&theora_data->ti);
64     theora_comment_init(&theora_data->tc);
65 
66     ogg_stream_packetout(&codec->os, &packet);
67 
68     if (theora_decode_header(&theora_data->ti, &theora_data->tc, &packet) < 0) {
69         free_theora_data(theora_data);
70         return SHOUTERR_UNSUPPORTED;
71     }
72 
73     codec->codec_data   = theora_data;
74     codec->read_page    = read_theora_page;
75     codec->free_data    = free_theora_data;
76     codec->headers      = 1;
77 
78     theora_data->initial_frames = 0;
79 
80     return SHOUTERR_SUCCESS;
81 }
82 
read_theora_page(ogg_codec_t * codec,ogg_page * page)83 static int read_theora_page(ogg_codec_t *codec, ogg_page *page)
84 {
85     theora_data_t  *theora_data = codec->codec_data;
86     ogg_packet      packet;
87     ogg_int64_t     granulepos, iframe, pframe;
88 
89     granulepos = ogg_page_granulepos(page);
90 
91 	if (granulepos == 0) {
92         while (ogg_stream_packetout(&codec->os, &packet) > 0) {
93 			if (theora_decode_header(&theora_data->ti, &theora_data->tc, &packet) < 0)
94                 return SHOUTERR_INSANE;
95             codec->headers++;
96         }
97 		if (codec->headers == 3) {
98             theora_data->granule_shift   = theora_ilog(theora_data->ti.keyframe_frequency_force - 1);
99             theora_data->per_frame       = (double)theora_data->ti.fps_denominator / theora_data->ti.fps_numerator * 1000000;
100             theora_data->get_start_frame = 1;
101         }
102 
103         return SHOUTERR_SUCCESS;
104     }
105 
106     while (ogg_stream_packetout(&codec->os, &packet) > 0) {
107         if (theora_data->get_start_frame)
108             theora_data->initial_frames++;
109     }
110 	if (granulepos > 0 && codec->headers >= 3) {
111         iframe = granulepos >> theora_data->granule_shift;
112         pframe = granulepos - (iframe << theora_data->granule_shift);
113 
114         if (theora_data->get_start_frame) {
115             /* work out the real start frame, which may not be 0 */
116             theora_data->start_frame = iframe + pframe - theora_data->initial_frames;
117             codec->senttime = 0;
118             theora_data->get_start_frame = 0;
119         } else {
120             uint64_t frames = ((iframe + pframe) - theora_data->start_frame);
121             codec->senttime = (uint64_t)(frames * theora_data->per_frame);
122         }
123     }
124     return SHOUTERR_SUCCESS;
125 }
126 
free_theora_data(void * codec_data)127 static void free_theora_data(void *codec_data)
128 {
129     theora_data_t *theora_data = (theora_data_t*)codec_data;
130 
131     theora_info_clear(&theora_data->ti);
132     theora_comment_clear(&theora_data->tc);
133     free(theora_data);
134 }
135 
theora_ilog(unsigned int v)136 static int theora_ilog(unsigned int v)
137 {
138     int ret = 0;
139 
140     while (v) {
141         ret++;
142         v >>= 1;
143     }
144 
145     return ret;
146 }
147