ogg.c 6.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * Ogg bitstream support
 * Mark Hills <mark@pogo.org.uk>
 *
 * Uses libogg, but requires libvorbisenc to construct correct headers
 * when containing Vorbis stream -- currently the only format supported
 */

#include <stdio.h>

#include <ogg/ogg.h>

#include "avformat.h"

15 16 17
#undef NDEBUG
#include <assert.h>

M
Mark Hills 已提交
18 19
#define DECODER_BUFFER_SIZE 4096

20 21

typedef struct OggContext {
M
Mark Hills 已提交
22
    /* output */
23
    ogg_stream_state os ;
M
Mark Hills 已提交
24
    int header_handled ;
25
    ogg_packet op;
M
Mark Hills 已提交
26 27 28

    /* input */
    ogg_sync_state oy ;
29 30 31
} OggContext ;


32
#ifdef CONFIG_ENCODERS
F
Fabrice Bellard 已提交
33 34 35
static int ogg_write_header(AVFormatContext *avfcontext) 
{
    OggContext *context = avfcontext->priv_data;
36 37
    ogg_packet *op= &context->op;    
    int n, i;
38 39 40

    av_set_pts_info(avfcontext, 60, 1, AV_TIME_BASE);

M
Michael Niedermayer 已提交
41
    ogg_stream_init(&context->os, 31415);
42 43
    
    for(n = 0 ; n < avfcontext->nb_streams ; n++) {
44 45 46 47 48 49 50 51 52 53 54 55 56 57
        AVCodecContext *codec = &avfcontext->streams[n]->codec;
        uint8_t *p= codec->extradata;
        
        for(i=0; i < codec->extradata_size; i+= op->bytes){
            op->bytes = p[i++]<<8;
            op->bytes+= p[i++];

            op->packet= &p[i];
            op->b_o_s= op->packetno==0;

            ogg_stream_packetin(&context->os, op);

            op->packetno++; //FIXME multiple streams
        }
58

M
Mark Hills 已提交
59
	context->header_handled = 0 ;
60 61 62 63 64 65 66
    }
    
    return 0 ;
}

static int ogg_write_packet(AVFormatContext *avfcontext,
			    int stream_index,
67
			    const uint8_t *buf, int size, int64_t pts)
68 69
{
    OggContext *context = avfcontext->priv_data ;
70 71
    AVCodecContext *avctx= &avfcontext->streams[stream_index]->codec;
    ogg_packet *op= &context->op;
72
    ogg_page og ;
73 74 75 76 77

    pts= av_rescale(pts, avctx->sample_rate, AV_TIME_BASE);

//    av_log(avfcontext, AV_LOG_DEBUG, "M%d\n", size);

78 79
    /* flush header packets so audio starts on a new page */

M
Mark Hills 已提交
80
    if(!context->header_handled) {
81 82 83 84 85
	while(ogg_stream_flush(&context->os, &og)) {
	    put_buffer(&avfcontext->pb, og.header, og.header_len) ;
	    put_buffer(&avfcontext->pb, og.body, og.body_len) ;
	    put_flush_packet(&avfcontext->pb);
	}
M
Mark Hills 已提交
86
	context->header_handled = 1 ;
87 88
    }

89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
    op->packet = (uint8_t*) buf;
    op->bytes  = size;
    op->b_o_s  = op->packetno == 0;
    op->granulepos= pts;

    /* correct the fields in the packet -- essential for streaming */
                                                        
    ogg_stream_packetin(&context->os, op);              
                                                        
    while(ogg_stream_pageout(&context->os, &og)) {
        put_buffer(&avfcontext->pb, og.header, og.header_len);
	put_buffer(&avfcontext->pb, og.body, og.body_len);     
	put_flush_packet(&avfcontext->pb);
    }                                                   
    op->packetno++;
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123

    return 0;
}


static int ogg_write_trailer(AVFormatContext *avfcontext) {
    OggContext *context = avfcontext->priv_data ;
    ogg_page og ;

    while(ogg_stream_flush(&context->os, &og)) {
	put_buffer(&avfcontext->pb, og.header, og.header_len) ;
	put_buffer(&avfcontext->pb, og.body, og.body_len) ;
	put_flush_packet(&avfcontext->pb);
    }

    ogg_stream_clear(&context->os) ;
    return 0 ;
}


Z
Zdenek Kabelac 已提交
124
static AVOutputFormat ogg_oformat = {
125 126 127 128 129 130 131 132 133 134
    "ogg",
    "Ogg Vorbis",
    "audio/x-vorbis",
    "ogg",
    sizeof(OggContext),
    CODEC_ID_VORBIS,
    0,
    ogg_write_header,
    ogg_write_packet,
    ogg_write_trailer,
M
Mark Hills 已提交
135
} ;
136
#endif //CONFIG_ENCODERS
M
Mark Hills 已提交
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164


static int next_packet(AVFormatContext *avfcontext, ogg_packet *op) {
    OggContext *context = avfcontext->priv_data ;
    ogg_page og ;
    char *buf ;

    while(ogg_stream_packetout(&context->os, op) != 1) {

	/* while no pages are available, read in more data to the sync */
	while(ogg_sync_pageout(&context->oy, &og) != 1) {
	    buf = ogg_sync_buffer(&context->oy, DECODER_BUFFER_SIZE) ;
	    if(get_buffer(&avfcontext->pb, buf, DECODER_BUFFER_SIZE) <= 0)
		return 1 ;
	    ogg_sync_wrote(&context->oy, DECODER_BUFFER_SIZE) ; 
	}	
	
	/* got a page. Feed it into the stream and get the packet */
	if(ogg_stream_pagein(&context->os, &og) != 0)
	    return 1 ;
    }

    return 0 ;
}


static int ogg_read_header(AVFormatContext *avfcontext, AVFormatParameters *ap)
{
F
Fabrice Bellard 已提交
165
    OggContext *context = avfcontext->priv_data;
166
    ogg_packet op ;    
M
Mark Hills 已提交
167 168 169
    char *buf ;
    ogg_page og ;
    AVStream *ast ;
170 171 172
    AVCodecContext *codec;
    uint8_t *p;
    int i;
M
Michael Niedermayer 已提交
173 174
     
    avfcontext->ctx_flags |= AVFMTCTX_NOHEADER;
M
Michael Niedermayer 已提交
175
    av_set_pts_info(avfcontext, 60, 1, AV_TIME_BASE);
M
Michael Niedermayer 已提交
176
     
M
Mark Hills 已提交
177 178 179 180 181 182 183 184 185 186
    ogg_sync_init(&context->oy) ;
    buf = ogg_sync_buffer(&context->oy, DECODER_BUFFER_SIZE) ;

    if(get_buffer(&avfcontext->pb, buf, DECODER_BUFFER_SIZE) <= 0)
	return -EIO ;
    
    ogg_sync_wrote(&context->oy, DECODER_BUFFER_SIZE) ;   
    ogg_sync_pageout(&context->oy, &og) ;
    ogg_stream_init(&context->os, ogg_page_serialno(&og)) ;
    ogg_stream_pagein(&context->os, &og) ;
187
    
M
Mark Hills 已提交
188 189 190 191 192 193
    /* currently only one vorbis stream supported */

    ast = av_new_stream(avfcontext, 0) ;
    if(!ast)
	return AVERROR_NOMEM ;

194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
    codec= &ast->codec;
    codec->codec_type = CODEC_TYPE_AUDIO;
    codec->codec_id = CODEC_ID_VORBIS;
    for(i=0; i<3; i++){
        if(next_packet(avfcontext, &op)){
            return -1;
        }
        codec->extradata_size+= 2 + op.bytes;
        codec->extradata= av_realloc(codec->extradata, codec->extradata_size);
        p= codec->extradata + codec->extradata_size - 2 - op.bytes;
        *(p++)= op.bytes>>8;
        *(p++)= op.bytes&0xFF;
        memcpy(p, op.packet, op.bytes);
    }

M
Mark Hills 已提交
209 210 211 212 213 214 215 216 217
    return 0 ;
}


static int ogg_read_packet(AVFormatContext *avfcontext, AVPacket *pkt) {
    ogg_packet op ;

    if(next_packet(avfcontext, &op)) 
	return -EIO ;
218
    if(av_new_packet(pkt, op.bytes) < 0)
M
Mark Hills 已提交
219 220
	return -EIO ;
    pkt->stream_index = 0 ;
221
    memcpy(pkt->data, op.packet, op.bytes);
M
Michael Niedermayer 已提交
222 223 224
    if(avfcontext->streams[0]->codec.sample_rate && op.granulepos!=-1)
        pkt->pts= av_rescale(op.granulepos, AV_TIME_BASE, avfcontext->streams[0]->codec.sample_rate);
//    printf("%lld %d %d\n", pkt->pts, (int)op.granulepos, avfcontext->streams[0]->codec.sample_rate);
M
Mark Hills 已提交
225

226
    return op.bytes;
M
Mark Hills 已提交
227 228 229 230 231 232 233 234
}


static int ogg_read_close(AVFormatContext *avfcontext) {
    OggContext *context = avfcontext->priv_data ;

    ogg_stream_clear(&context->os) ;
    ogg_sync_clear(&context->oy) ;
235
    av_freep(&avfcontext->streams[0]->codec.extradata);
M
Mark Hills 已提交
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250

    return 0 ;
}


static AVInputFormat ogg_iformat = {
    "ogg",
    "Ogg Vorbis",
    sizeof(OggContext),
    NULL,
    ogg_read_header,
    ogg_read_packet,
    ogg_read_close,
    .extensions = "ogg",
} ;
251 252 253


int ogg_init(void) {
254
#ifdef CONFIG_ENCODERS
255
    av_register_output_format(&ogg_oformat) ;
256
#endif
M
Mark Hills 已提交
257
    av_register_input_format(&ogg_iformat);
258 259
    return 0 ;
}