2014-02-10 01:10:30 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2022-09-28 00:18:11 +00:00
|
|
|
#include "state.h"
|
2014-02-10 01:10:30 +00:00
|
|
|
#include "enquant.h"
|
|
|
|
#include "huffenc.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*Packs a series of octets from a given byte array into the pack buffer.
|
|
|
|
_opb: The pack buffer to store the octets in.
|
|
|
|
_buf: The byte array containing the bytes to pack.
|
|
|
|
_len: The number of octets to pack.*/
|
|
|
|
static void oc_pack_octets(oggpack_buffer *_opb,const char *_buf,int _len){
|
|
|
|
int i;
|
|
|
|
for(i=0;i<_len;i++)oggpackB_write(_opb,_buf[i],8);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int oc_state_flushheader(oc_theora_state *_state,int *_packet_state,
|
|
|
|
oggpack_buffer *_opb,const th_quant_info *_qinfo,
|
|
|
|
const th_huff_code _codes[TH_NHUFFMAN_TABLES][TH_NDCT_TOKENS],
|
|
|
|
const char *_vendor,th_comment *_tc,ogg_packet *_op){
|
|
|
|
unsigned char *packet;
|
|
|
|
int b_o_s;
|
|
|
|
if(_op==NULL)return TH_EFAULT;
|
|
|
|
switch(*_packet_state){
|
|
|
|
/*Codec info header.*/
|
|
|
|
case OC_PACKET_INFO_HDR:{
|
|
|
|
if(_state==NULL)return TH_EFAULT;
|
|
|
|
oggpackB_reset(_opb);
|
|
|
|
/*Mark this packet as the info header.*/
|
|
|
|
oggpackB_write(_opb,0x80,8);
|
|
|
|
/*Write the codec string.*/
|
|
|
|
oc_pack_octets(_opb,"theora",6);
|
|
|
|
/*Write the codec bitstream version.*/
|
|
|
|
oggpackB_write(_opb,TH_VERSION_MAJOR,8);
|
|
|
|
oggpackB_write(_opb,TH_VERSION_MINOR,8);
|
|
|
|
oggpackB_write(_opb,TH_VERSION_SUB,8);
|
|
|
|
/*Describe the encoded frame.*/
|
|
|
|
oggpackB_write(_opb,_state->info.frame_width>>4,16);
|
|
|
|
oggpackB_write(_opb,_state->info.frame_height>>4,16);
|
|
|
|
oggpackB_write(_opb,_state->info.pic_width,24);
|
|
|
|
oggpackB_write(_opb,_state->info.pic_height,24);
|
|
|
|
oggpackB_write(_opb,_state->info.pic_x,8);
|
|
|
|
oggpackB_write(_opb,_state->info.pic_y,8);
|
|
|
|
oggpackB_write(_opb,_state->info.fps_numerator,32);
|
|
|
|
oggpackB_write(_opb,_state->info.fps_denominator,32);
|
|
|
|
oggpackB_write(_opb,_state->info.aspect_numerator,24);
|
|
|
|
oggpackB_write(_opb,_state->info.aspect_denominator,24);
|
|
|
|
oggpackB_write(_opb,_state->info.colorspace,8);
|
|
|
|
oggpackB_write(_opb,_state->info.target_bitrate,24);
|
|
|
|
oggpackB_write(_opb,_state->info.quality,6);
|
|
|
|
oggpackB_write(_opb,_state->info.keyframe_granule_shift,5);
|
|
|
|
oggpackB_write(_opb,_state->info.pixel_fmt,2);
|
|
|
|
/*Spare configuration bits.*/
|
|
|
|
oggpackB_write(_opb,0,3);
|
|
|
|
b_o_s=1;
|
|
|
|
}break;
|
|
|
|
/*Comment header.*/
|
|
|
|
case OC_PACKET_COMMENT_HDR:{
|
|
|
|
int vendor_len;
|
|
|
|
int i;
|
|
|
|
if(_tc==NULL)return TH_EFAULT;
|
|
|
|
vendor_len=strlen(_vendor);
|
|
|
|
oggpackB_reset(_opb);
|
|
|
|
/*Mark this packet as the comment header.*/
|
|
|
|
oggpackB_write(_opb,0x81,8);
|
|
|
|
/*Write the codec string.*/
|
|
|
|
oc_pack_octets(_opb,"theora",6);
|
|
|
|
/*Write the vendor string.*/
|
|
|
|
oggpack_write(_opb,vendor_len,32);
|
|
|
|
oc_pack_octets(_opb,_vendor,vendor_len);
|
|
|
|
oggpack_write(_opb,_tc->comments,32);
|
|
|
|
for(i=0;i<_tc->comments;i++){
|
|
|
|
if(_tc->user_comments[i]!=NULL){
|
|
|
|
oggpack_write(_opb,_tc->comment_lengths[i],32);
|
|
|
|
oc_pack_octets(_opb,_tc->user_comments[i],_tc->comment_lengths[i]);
|
|
|
|
}
|
|
|
|
else oggpack_write(_opb,0,32);
|
|
|
|
}
|
|
|
|
b_o_s=0;
|
|
|
|
}break;
|
|
|
|
/*Codec setup header.*/
|
|
|
|
case OC_PACKET_SETUP_HDR:{
|
|
|
|
int ret;
|
|
|
|
oggpackB_reset(_opb);
|
|
|
|
/*Mark this packet as the setup header.*/
|
|
|
|
oggpackB_write(_opb,0x82,8);
|
|
|
|
/*Write the codec string.*/
|
|
|
|
oc_pack_octets(_opb,"theora",6);
|
|
|
|
/*Write the quantizer tables.*/
|
|
|
|
oc_quant_params_pack(_opb,_qinfo);
|
|
|
|
/*Write the huffman codes.*/
|
|
|
|
ret=oc_huff_codes_pack(_opb,_codes);
|
|
|
|
/*This should never happen, because we validate the tables when they
|
|
|
|
are set.
|
|
|
|
If you see, it's a good chance memory is being corrupted.*/
|
|
|
|
if(ret<0)return ret;
|
|
|
|
b_o_s=0;
|
|
|
|
}break;
|
|
|
|
/*No more headers to emit.*/
|
|
|
|
default:return 0;
|
|
|
|
}
|
|
|
|
/*This is kind of fugly: we hand the user a buffer which they do not own.
|
|
|
|
We will overwrite it when the next packet is output, so the user better be
|
|
|
|
done with it by then.
|
|
|
|
Vorbis is little better: it hands back buffers that it will free the next
|
|
|
|
time the headers are requested, or when the encoder is cleared.
|
|
|
|
Hopefully libogg2 will make this much cleaner.*/
|
|
|
|
packet=oggpackB_get_buffer(_opb);
|
|
|
|
/*If there's no packet, malloc failed while writing.*/
|
|
|
|
if(packet==NULL)return TH_EFAULT;
|
|
|
|
_op->packet=packet;
|
|
|
|
_op->bytes=oggpackB_bytes(_opb);
|
|
|
|
_op->b_o_s=b_o_s;
|
|
|
|
_op->e_o_s=0;
|
|
|
|
_op->granulepos=0;
|
|
|
|
_op->packetno=*_packet_state+3;
|
|
|
|
return ++(*_packet_state)+3;
|
|
|
|
}
|