summaryrefslogtreecommitdiff
path: root/lib/encapiwrapper.c
blob: 874f12442d43dc05824666d58a8dcd3f42e754a8 (plain)
1
2
3
4
5
6
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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
165
166
167
168
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "apiwrapper.h"
#include "encint.h"
#include "theora/theoraenc.h"



static void th_enc_api_clear(th_api_wrapper *_api){
  if(_api->encode)th_encode_free(_api->encode);
  memset(_api,0,sizeof(*_api));
}

static void theora_encode_clear(theora_state *_te){
  if(_te->i!=NULL)theora_info_clear(_te->i);
  memset(_te,0,sizeof(*_te));
}

static int theora_encode_control(theora_state *_te,int _req,
 void *_buf,size_t _buf_sz){
  return th_encode_ctl(((th_api_wrapper *)_te->i->codec_setup)->encode,
   _req,_buf,_buf_sz);
}

static ogg_int64_t theora_encode_granule_frame(theora_state *_te,
 ogg_int64_t _gp){
  return th_granule_frame(((th_api_wrapper *)_te->i->codec_setup)->encode,_gp);
}

static double theora_encode_granule_time(theora_state *_te,ogg_int64_t _gp){
  return th_granule_time(((th_api_wrapper *)_te->i->codec_setup)->encode,_gp);
}

static const oc_state_dispatch_vtable OC_ENC_DISPATCH_VTBL={
  (oc_state_clear_func)theora_encode_clear,
  (oc_state_control_func)theora_encode_control,
  (oc_state_granule_frame_func)theora_encode_granule_frame,
  (oc_state_granule_time_func)theora_encode_granule_time,
};

int theora_encode_init(theora_state *_te,theora_info *_ci){
  th_api_info *apiinfo;
  th_info      info;
  ogg_uint32_t keyframe_frequency_force;
  /*Allocate our own combined API wrapper/theora_info struct.
    We put them both in one malloc'd block so that when the API wrapper is
     freed, the info struct goes with it.
    This avoids having to figure out whether or not we need to free the info
     struct in either theora_info_clear() or theora_clear().*/
  apiinfo=(th_api_info *)_ogg_malloc(sizeof(*apiinfo));
  if(apiinfo==NULL)return TH_EFAULT;
  /*Make our own copy of the info struct, since its lifetime should be
     independent of the one we were passed in.*/
  *&apiinfo->info=*_ci;
  oc_theora_info2th_info(&info,_ci);
  apiinfo->api.encode=th_encode_alloc(&info);
  if(apiinfo->api.encode==NULL){
    _ogg_free(apiinfo);
    return OC_EINVAL;
  }
  apiinfo->api.clear=(oc_setup_clear_func)th_enc_api_clear;
  /*Provide entry points for ABI compatibility with old decoder shared libs.*/
  _te->internal_encode=(void *)&OC_ENC_DISPATCH_VTBL;
  _te->internal_decode=NULL;
  _te->granulepos=0;
  _te->i=&apiinfo->info;
  _te->i->codec_setup=&apiinfo->api;
  /*Set the precise requested keyframe frequency.*/
  keyframe_frequency_force=_ci->keyframe_auto_p?
   _ci->keyframe_frequency_force:_ci->keyframe_frequency;
  th_encode_ctl(apiinfo->api.encode,
   TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,
   &keyframe_frequency_force,sizeof(keyframe_frequency_force));
  /*TODO: Additional codec setup using the extra fields in theora_info.*/
  return 0;
}

int theora_encode_YUVin(theora_state *_te,yuv_buffer *_yuv){
  th_api_wrapper  *api;
  th_ycbcr_buffer  buf;
  int              ret;
  api=(th_api_wrapper *)_te->i->codec_setup;
  buf[0].width=_yuv->y_width;
  buf[0].height=_yuv->y_height;
  buf[0].stride=_yuv->y_stride;
  buf[0].data=_yuv->y;
  buf[1].width=_yuv->uv_width;
  buf[1].height=_yuv->uv_height;
  buf[1].stride=_yuv->uv_stride;
  buf[1].data=_yuv->u;
  buf[2].width=_yuv->uv_width;
  buf[2].height=_yuv->uv_height;
  buf[2].stride=_yuv->uv_stride;
  buf[2].data=_yuv->v;
  ret=th_encode_ycbcr_in(api->encode,buf);
  if(ret<0)return ret;
  _te->granulepos=api->encode->state.granpos;
  return ret;
}

int theora_encode_packetout(theora_state *_te,int _last_p,ogg_packet *_op){
  th_api_wrapper *api;
  api=(th_api_wrapper *)_te->i->codec_setup;
  return th_encode_packetout(api->encode,_last_p,_op);
}

int theora_encode_header(theora_state *_te,ogg_packet *_op){
  oc_enc_ctx     *enc;
  th_api_wrapper *api;
  int             ret;
  api=(th_api_wrapper *)_te->i->codec_setup;
  enc=api->encode;
  /*If we've already started encoding, fail.*/
  if(enc->packet_state>OC_PACKET_EMPTY||enc->state.granpos!=0){
    return TH_EINVAL;
  }
  /*Reset the state to make sure we output an info packet.*/
  enc->packet_state=OC_PACKET_INFO_HDR;
  ret=th_encode_flushheader(api->encode,NULL,_op);
  return ret>=0?0:ret;
}

int theora_encode_comment(theora_comment *_tc,ogg_packet *_op){
  oggpack_buffer  opb;
  void           *buf;
  int             packet_state;
  int             ret;
  packet_state=OC_PACKET_COMMENT_HDR;
  oggpackB_writeinit(&opb);
  ret=oc_state_flushheader(NULL,&packet_state,&opb,NULL,NULL,
   th_version_string(),(th_comment *)_tc,_op);
  if(ret>=0){
    /*The oggpack_buffer's lifetime ends with this function, so we have to
       copy out the packet contents.
      Presumably the application knows it is supposed to free this.
      This part works nothing like the Vorbis API, and the documentation on it
       has been wrong for some time, claiming libtheora owned the memory.*/
    buf=_ogg_malloc(_op->bytes);
    if(buf==NULL){
      _op->packet=NULL;
      ret=TH_EFAULT;
    }
    else{
      memcpy(buf,_op->packet,_op->bytes);
      _op->packet=buf;
      ret=0;
    }
  }
  oggpack_writeclear(&opb);
  return ret;
}

int theora_encode_tables(theora_state *_te,ogg_packet *_op){
  oc_enc_ctx     *enc;
  th_api_wrapper *api;
  int             ret;
  api=(th_api_wrapper *)_te->i->codec_setup;
  enc=api->encode;
  /*If we've already started encoding, fail.*/
  if(enc->packet_state>OC_PACKET_EMPTY||enc->state.granpos!=0){
    return TH_EINVAL;
  }
  /*Reset the state to make sure we output a setup packet.*/
  enc->packet_state=OC_PACKET_SETUP_HDR;
  ret=th_encode_flushheader(api->encode,NULL,_op);
  return ret>=0?0:ret;
}