format_theora.c 5.5 KB
Newer Older
1 2 3 4 5
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
6
 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
 *                      Michael Smith <msmith@xiph.org>,
 *                      oddsock <oddsock@xiph.org>,
 *                      Karl Heyes <karl@xiph.org>
 *                      and others (see AUTHORS for details).
 */


/* Ogg codec handler for theora logical streams */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>
#include <ogg/ogg.h>
#include <theora/theora.h>

#include "refbuf.h"
#include "format_ogg.h"
#include "format_theora.h"
#include "client.h"
#include "stats.h"

#define CATMODULE "format-theora"
#include "logging.h"


typedef struct _theora_codec_tag
{
    theora_info     ti;
    theora_comment  tc;
    int             granule_shift;
    ogg_int64_t     last_iframe;
    ogg_int64_t     prev_granulepos;
} theora_codec_t;


static void theora_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec)
{
    theora_codec_t *theora = codec->specific;

48
    ICECAST_LOG_DEBUG("freeing theora codec");
49
    stats_event (ogg_info->mount, "video_bitrate", NULL);
j's avatar
j committed
50 51
    stats_event (ogg_info->mount, "video_quality", NULL);
    stats_event (ogg_info->mount, "frame_rate", NULL);
52 53 54 55 56 57 58 59 60 61 62 63
    stats_event (ogg_info->mount, "frame_size", NULL);
    theora_info_clear (&theora->ti);
    theora_comment_clear (&theora->tc);
    ogg_stream_clear (&codec->os);
    free (theora);
    free (codec);
}


/* theora pages are not rebuilt, so here we just for headers and then
 * pass them straight through to the the queue
 */
64
static refbuf_t *process_theora_page (ogg_state_t *ogg_info, ogg_codec_t *codec, ogg_page *page, format_plugin_t *plugin)
65 66 67 68 69 70 71 72 73 74 75 76 77
{
    theora_codec_t *theora = codec->specific;
    ogg_packet packet;
    int header_page = 0;
    int has_keyframe = 0;
    refbuf_t *refbuf = NULL;
    ogg_int64_t granulepos;

    if (ogg_stream_pagein (&codec->os, page) < 0)
    {
        ogg_info->error = 1;
        return NULL;
    }
78
    granulepos = ogg_page_granulepos(page);
79 80 81 82 83 84 85 86

    while (ogg_stream_packetout (&codec->os, &packet) > 0)
    {
        if (theora_packet_isheader (&packet))
        {
            if (theora_decode_header (&theora->ti, &theora->tc, &packet) < 0)
            {
                ogg_info->error = 1;
87
                ICECAST_LOG_WARN("problem with theora header");
88 89 90 91
                return NULL;
            }
            header_page = 1;
            codec->headers++;
Karl Heyes's avatar
Karl Heyes committed
92 93 94 95 96
            if (codec->headers == 3)
            {
                ogg_info->bitrate += theora->ti.target_bitrate;
                stats_event_args (ogg_info->mount, "video_bitrate", "%ld",
                        (long)theora->ti.target_bitrate);
j's avatar
j committed
97 98
                stats_event_args (ogg_info->mount, "video_quality", "%ld",
                        (long)theora->ti.quality);
Karl Heyes's avatar
Karl Heyes committed
99 100 101
                stats_event_args (ogg_info->mount, "frame_size", "%ld x %ld",
                        (long)theora->ti.frame_width,
                        (long)theora->ti.frame_height);
j's avatar
j committed
102
                stats_event_args (ogg_info->mount, "frame_rate", "%.2f",
Karl Heyes's avatar
Karl Heyes committed
103 104
                        (float)theora->ti.fps_numerator/theora->ti.fps_denominator);
            }
105 106 107 108 109
            continue;
        }
        if (codec->headers < 3)
        {
            ogg_info->error = 1;
110
            ICECAST_LOG_ERROR("Not enough header packets");
111 112 113 114 115 116 117 118 119 120 121 122
            return NULL;
        }
        if (theora_packet_iskeyframe (&packet))
            has_keyframe = 1;
    }
    if (header_page)
    {
        format_ogg_attach_header (ogg_info, page);
        return NULL;
    }

    refbuf = make_refbuf_with_page (page);
123
    /* ICECAST_LOG_DEBUG("refbuf %p has pageno %ld, %llu", refbuf, ogg_page_pageno (page), (uint64_t)granulepos); */
124 125 126 127 128 129 130 131 132

    if (granulepos != theora->prev_granulepos || granulepos == 0)
    {
        if (codec->possible_start)
            refbuf_release (codec->possible_start);
        refbuf_addref (refbuf);
        codec->possible_start = refbuf;
    }
    theora->prev_granulepos = granulepos;
133 134 135 136 137 138
    if (has_keyframe && codec->possible_start)
    {
        codec->possible_start->sync_point = 1;
        refbuf_release (codec->possible_start);
        codec->possible_start = NULL;
    }
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162

    return refbuf;
}


/* Check if specified BOS page is the start of a theora stream and
 * if so, create a codec structure for handling it
 */
ogg_codec_t *initial_theora_page (format_plugin_t *plugin, ogg_page *page)
{
    ogg_state_t *ogg_info = plugin->_state;
    ogg_codec_t *codec = calloc (1, sizeof (ogg_codec_t));
    ogg_packet packet;

    theora_codec_t *theora_codec = calloc (1, sizeof (theora_codec_t));

    ogg_stream_init (&codec->os, ogg_page_serialno (page));
    ogg_stream_pagein (&codec->os, page);

    theora_info_init (&theora_codec->ti);
    theora_comment_init (&theora_codec->tc);

    ogg_stream_packetout (&codec->os, &packet);

163
    ICECAST_LOG_DEBUG("checking for theora codec");
164 165 166 167 168 169 170 171 172
    if (theora_decode_header (&theora_codec->ti, &theora_codec->tc, &packet) < 0)
    {
        theora_info_clear (&theora_codec->ti);
        theora_comment_clear (&theora_codec->tc);
        ogg_stream_clear (&codec->os);
        free (theora_codec);
        free (codec);
        return NULL;
    }
173
    ICECAST_LOG_INFO("seen initial theora header");
174 175 176 177
    codec->specific = theora_codec;
    codec->process_page = process_theora_page;
    codec->codec_free = theora_codec_free;
    codec->headers = 1;
178 179
    codec->name = "Theora";

180
    format_ogg_attach_header(ogg_info, page);
181 182 183 184
    ogg_info->codec_sync = codec;
    return codec;
}