ogg.c 7.2 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_MUXERS
F
Fabrice Bellard 已提交
33 34 35
static int ogg_write_header(AVFormatContext *avfcontext) 
{
    OggContext *context = avfcontext->priv_data;
36
    ogg_packet *op= &context->op;    
A
Aurelien Jacobs 已提交
37
    int n;
38

M
Michael Niedermayer 已提交
39
    ogg_stream_init(&context->os, 31415);
40 41
    
    for(n = 0 ; n < avfcontext->nb_streams ; n++) {
42
        AVCodecContext *codec = avfcontext->streams[n]->codec;
43 44 45 46
        uint8_t *headers = codec->extradata;
        int headers_len = codec->extradata_size;
        uint8_t *header_start[3];
        int header_len[3];
A
Aurelien Jacobs 已提交
47
        int i, j;
48
        
49 50
        av_set_pts_info(avfcontext->streams[n], 60, 1, AV_TIME_BASE);

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
        for(j=1,i=0;i<2;++i, ++j) {
            header_len[i]=0;
            while(j<headers_len && headers[j]==0xff) {
                header_len[i]+=0xff;
                ++j;
            }
            header_len[i]+=headers[j];
        }
        header_len[2]=headers_len-header_len[0]-header_len[1]-j;
        headers+=j;
        header_start[0] = headers;
        header_start[1] = header_start[0] + header_len[0];
        header_start[2] = header_start[1] + header_len[1];

        for(i=0; i < 3; ++i){
            op->bytes = header_len[i];
67

68
            op->packet= header_start[i];
69 70 71 72 73 74
            op->b_o_s= op->packetno==0;

            ogg_stream_packetin(&context->os, op);

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

M
Mark Hills 已提交
76
	context->header_handled = 0 ;
77 78 79 80 81
    }
    
    return 0 ;
}

82
static int ogg_write_packet(AVFormatContext *avfcontext, AVPacket *pkt)
83 84
{
    OggContext *context = avfcontext->priv_data ;
85
    AVCodecContext *avctx= avfcontext->streams[pkt->stream_index]->codec;
86
    ogg_packet *op= &context->op;
87
    ogg_page og ;
88
    int64_t pts;
89

90
    pts= av_rescale(pkt->pts, avctx->sample_rate, AV_TIME_BASE);
91 92 93

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

94 95
    /* flush header packets so audio starts on a new page */

M
Mark Hills 已提交
96
    if(!context->header_handled) {
97 98 99 100 101
	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 已提交
102
	context->header_handled = 1 ;
103 104
    }

105 106
    op->packet = (uint8_t*) pkt->data;
    op->bytes  = pkt->size;
107 108 109 110 111 112 113 114 115 116 117 118 119
    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++;
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139

    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 已提交
140
static AVOutputFormat ogg_oformat = {
141 142 143 144 145 146 147 148 149 150
    "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 已提交
151
} ;
152
#endif //CONFIG_MUXERS
M
Mark Hills 已提交
153

154
#if 0
M
Mark Hills 已提交
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
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 已提交
181
    OggContext *context = avfcontext->priv_data;
182
    ogg_packet op ;    
M
Mark Hills 已提交
183 184 185
    char *buf ;
    ogg_page og ;
    AVStream *ast ;
186 187 188
    AVCodecContext *codec;
    uint8_t *p;
    int i;
M
Michael Niedermayer 已提交
189
     
M
Mark Hills 已提交
190 191 192 193
    ogg_sync_init(&context->oy) ;
    buf = ogg_sync_buffer(&context->oy, DECODER_BUFFER_SIZE) ;

    if(get_buffer(&avfcontext->pb, buf, DECODER_BUFFER_SIZE) <= 0)
194
	return AVERROR_IO ;
M
Mark Hills 已提交
195 196 197 198 199
    
    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) ;
200
    
M
Mark Hills 已提交
201 202 203 204 205
    /* currently only one vorbis stream supported */

    ast = av_new_stream(avfcontext, 0) ;
    if(!ast)
	return AVERROR_NOMEM ;
206
    av_set_pts_info(ast, 60, 1, AV_TIME_BASE);
M
Mark Hills 已提交
207

208 209 210 211 212 213 214
    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;
        }
215 216
        if(op.bytes >= (1<<16) || op.bytes < 0)
            return -1;
217
        codec->extradata_size+= 2 + op.bytes;
218
        codec->extradata= av_realloc(codec->extradata, codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
219 220 221 222 223 224
        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 已提交
225 226 227 228 229 230 231 232
    return 0 ;
}


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

    if(next_packet(avfcontext, &op)) 
233
	return AVERROR_IO ;
234
    if(av_new_packet(pkt, op.bytes) < 0)
235
	return AVERROR_IO ;
M
Mark Hills 已提交
236
    pkt->stream_index = 0 ;
237
    memcpy(pkt->data, op.packet, op.bytes);
M
Michael Niedermayer 已提交
238 239 240
    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 已提交
241

242
    return op.bytes;
M
Mark Hills 已提交
243 244 245 246 247 248 249 250
}


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

    ogg_stream_clear(&context->os) ;
    ogg_sync_clear(&context->oy) ;
251
    av_freep(&avfcontext->streams[0]->codec.extradata);
M
Mark Hills 已提交
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266

    return 0 ;
}


static AVInputFormat ogg_iformat = {
    "ogg",
    "Ogg Vorbis",
    sizeof(OggContext),
    NULL,
    ogg_read_header,
    ogg_read_packet,
    ogg_read_close,
    .extensions = "ogg",
} ;
267
#endif
268

269
int libogg_init(void) {
270
#ifdef CONFIG_MUXERS
271
    av_register_output_format(&ogg_oformat) ;
272
#endif
273
/*     av_register_input_format(&ogg_iformat); */
274 275
    return 0 ;
}