PageRenderTime 57ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/pyvorbis-1.5a/src/vcedit.c

#
C | 484 lines | 390 code | 69 blank | 25 comment | 74 complexity | 6eff374d8bd8f46edebe1a6dbd1bab64 MD5 | raw file
Possible License(s): LGPL-2.0
  1. /* This program is licensed under the GNU Library General Public License, version 2,
  2. * a copy of which is included with this program (LICENCE.LGPL).
  3. *
  4. * (c) 2000-2001 Michael Smith <msmith@labyrinth.net.au>
  5. *
  6. *
  7. * Comment editing backend, suitable for use by nice frontend interfaces.
  8. *
  9. * last modified: $Id: vcedit.c,v 1.1 2002/01/22 01:57:56 andrew Exp $
  10. */
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <ogg/ogg.h>
  15. #include <vorbis/codec.h>
  16. #include "vcedit.h"
  17. #define CHUNKSIZE 4096
  18. vcedit_state *vcedit_new_state(void)
  19. {
  20. vcedit_state *state = malloc(sizeof(vcedit_state));
  21. memset(state, 0, sizeof(vcedit_state));
  22. return state;
  23. }
  24. char *vcedit_error(vcedit_state *state)
  25. {
  26. return state->lasterror;
  27. }
  28. vorbis_comment *vcedit_comments(vcedit_state *state)
  29. {
  30. return state->vc;
  31. }
  32. static void vcedit_clear_internals(vcedit_state *state)
  33. {
  34. if(state->vc)
  35. {
  36. vorbis_comment_clear(state->vc);
  37. free(state->vc);
  38. state->vc=NULL;
  39. }
  40. if(state->os)
  41. {
  42. ogg_stream_clear(state->os);
  43. free(state->os);
  44. state->os=NULL;
  45. }
  46. if(state->oy)
  47. {
  48. ogg_sync_clear(state->oy);
  49. free(state->oy);
  50. state->oy=NULL;
  51. }
  52. if(state->vendor)
  53. {
  54. free(state->vendor);
  55. state->vendor=NULL;
  56. }
  57. }
  58. void vcedit_clear(vcedit_state *state)
  59. {
  60. if(state)
  61. {
  62. vcedit_clear_internals(state);
  63. free(state);
  64. }
  65. }
  66. /* Next two functions pulled straight from libvorbis, apart from one change
  67. * - we don't want to overwrite the vendor string.
  68. */
  69. static void _v_writestring(oggpack_buffer *o,char *s, int len)
  70. {
  71. while(len--)
  72. {
  73. oggpack_write(o,*s++,8);
  74. }
  75. }
  76. static int _commentheader_out(vorbis_comment *vc, char *vendor, ogg_packet *op)
  77. {
  78. oggpack_buffer opb;
  79. oggpack_writeinit(&opb);
  80. /* preamble */
  81. oggpack_write(&opb,0x03,8);
  82. _v_writestring(&opb,"vorbis", 6);
  83. /* vendor */
  84. oggpack_write(&opb,strlen(vendor),32);
  85. _v_writestring(&opb,vendor, strlen(vendor));
  86. /* comments */
  87. oggpack_write(&opb,vc->comments,32);
  88. if(vc->comments){
  89. int i;
  90. for(i=0;i<vc->comments;i++){
  91. if(vc->user_comments[i]){
  92. oggpack_write(&opb,vc->comment_lengths[i],32);
  93. _v_writestring(&opb,vc->user_comments[i],
  94. vc->comment_lengths[i]);
  95. }else{
  96. oggpack_write(&opb,0,32);
  97. }
  98. }
  99. }
  100. oggpack_write(&opb,1,1);
  101. op->packet = _ogg_malloc(oggpack_bytes(&opb));
  102. memcpy(op->packet, opb.buffer, oggpack_bytes(&opb));
  103. op->bytes=oggpack_bytes(&opb);
  104. op->b_o_s=0;
  105. op->e_o_s=0;
  106. op->granulepos=0;
  107. return 0;
  108. }
  109. static int _blocksize(vcedit_state *s, ogg_packet *p)
  110. {
  111. int this = vorbis_packet_blocksize(&s->vi, p);
  112. int ret = (this + s->prevW)/4;
  113. if(!s->prevW)
  114. {
  115. s->prevW = this;
  116. return 0;
  117. }
  118. s->prevW = this;
  119. return ret;
  120. }
  121. static int _fetch_next_packet(vcedit_state *s, ogg_packet *p, ogg_page *page)
  122. {
  123. int result;
  124. char *buffer;
  125. int bytes;
  126. result = ogg_stream_packetout(s->os, p);
  127. if(result > 0)
  128. return 1;
  129. else
  130. {
  131. if(s->eosin)
  132. return 0;
  133. while(ogg_sync_pageout(s->oy, page) <= 0)
  134. {
  135. buffer = ogg_sync_buffer(s->oy, CHUNKSIZE);
  136. bytes = s->read(buffer,1, CHUNKSIZE, s->in);
  137. ogg_sync_wrote(s->oy, bytes);
  138. if(bytes == 0)
  139. return 0;
  140. }
  141. if(ogg_page_eos(page))
  142. s->eosin = 1;
  143. else if(ogg_page_serialno(page) != s->serial)
  144. {
  145. s->eosin = 1;
  146. s->extrapage = 1;
  147. return 0;
  148. }
  149. ogg_stream_pagein(s->os, page);
  150. return _fetch_next_packet(s, p, page);
  151. }
  152. }
  153. int vcedit_open(vcedit_state *state, FILE *in)
  154. {
  155. return vcedit_open_callbacks(state, (void *)in,
  156. (vcedit_read_func)fread, (vcedit_write_func)fwrite);
  157. }
  158. int vcedit_open_callbacks(vcedit_state *state, void *in,
  159. vcedit_read_func read_func, vcedit_write_func write_func)
  160. {
  161. char *buffer;
  162. int bytes,i;
  163. ogg_packet *header;
  164. ogg_packet header_main;
  165. ogg_packet header_comments;
  166. ogg_packet header_codebooks;
  167. ogg_page og;
  168. state->in = in;
  169. state->read = read_func;
  170. state->write = write_func;
  171. state->oy = malloc(sizeof(ogg_sync_state));
  172. ogg_sync_init(state->oy);
  173. buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
  174. bytes = state->read(buffer, 1, CHUNKSIZE, state->in);
  175. ogg_sync_wrote(state->oy, bytes);
  176. if(ogg_sync_pageout(state->oy, &og) != 1)
  177. {
  178. if(bytes<CHUNKSIZE)
  179. state->lasterror = "Input truncated or empty.";
  180. else
  181. state->lasterror = "Input is not an Ogg bitstream.";
  182. goto err;
  183. }
  184. state->serial = ogg_page_serialno(&og);
  185. state->os = malloc(sizeof(ogg_stream_state));
  186. ogg_stream_init(state->os, state->serial);
  187. vorbis_info_init(&state->vi);
  188. state->vc = malloc(sizeof(vorbis_comment));
  189. vorbis_comment_init(state->vc);
  190. if(ogg_stream_pagein(state->os, &og) < 0)
  191. {
  192. state->lasterror = "Error reading first page of Ogg bitstream.";
  193. goto err;
  194. }
  195. if(ogg_stream_packetout(state->os, &header_main) != 1)
  196. {
  197. state->lasterror = "Error reading initial header packet.";
  198. goto err;
  199. }
  200. if(vorbis_synthesis_headerin(&state->vi, state->vc, &header_main) < 0)
  201. {
  202. state->lasterror = "Ogg bitstream does not contain vorbis data.";
  203. goto err;
  204. }
  205. state->mainlen = header_main.bytes;
  206. state->mainbuf = malloc(state->mainlen);
  207. memcpy(state->mainbuf, header_main.packet, header_main.bytes);
  208. i = 0;
  209. header = &header_comments;
  210. while(i<2) {
  211. while(i<2) {
  212. int result = ogg_sync_pageout(state->oy, &og);
  213. if(result == 0) break; /* Too little data so far */
  214. else if(result == 1)
  215. {
  216. ogg_stream_pagein(state->os, &og);
  217. while(i<2)
  218. {
  219. result = ogg_stream_packetout(state->os, header);
  220. if(result == 0) break;
  221. if(result == -1)
  222. {
  223. state->lasterror = "Corrupt secondary header.";
  224. goto err;
  225. }
  226. vorbis_synthesis_headerin(&state->vi, state->vc, header);
  227. if(i==1)
  228. {
  229. state->booklen = header->bytes;
  230. state->bookbuf = malloc(state->booklen);
  231. memcpy(state->bookbuf, header->packet,
  232. header->bytes);
  233. }
  234. i++;
  235. header = &header_codebooks;
  236. }
  237. }
  238. }
  239. buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
  240. bytes = state->read(buffer, 1, CHUNKSIZE, state->in);
  241. if(bytes == 0 && i < 2)
  242. {
  243. state->lasterror = "EOF before end of vorbis headers.";
  244. goto err;
  245. }
  246. ogg_sync_wrote(state->oy, bytes);
  247. }
  248. /* Copy the vendor tag */
  249. state->vendor = malloc(strlen(state->vc->vendor) +1);
  250. strcpy(state->vendor, state->vc->vendor);
  251. /* Headers are done! */
  252. return 0;
  253. err:
  254. vcedit_clear_internals(state);
  255. return -1;
  256. }
  257. int vcedit_write(vcedit_state *state, void *out)
  258. {
  259. ogg_stream_state streamout;
  260. ogg_packet header_main;
  261. ogg_packet header_comments;
  262. ogg_packet header_codebooks;
  263. ogg_page ogout, ogin;
  264. ogg_packet op;
  265. ogg_int64_t granpos = 0;
  266. int result;
  267. char *buffer;
  268. int bytes;
  269. int needflush=0, needout=0;
  270. state->eosin = 0;
  271. state->extrapage = 0;
  272. header_main.bytes = state->mainlen;
  273. header_main.packet = state->mainbuf;
  274. header_main.b_o_s = 1;
  275. header_main.e_o_s = 0;
  276. header_main.granulepos = 0;
  277. header_codebooks.bytes = state->booklen;
  278. header_codebooks.packet = state->bookbuf;
  279. header_codebooks.b_o_s = 0;
  280. header_codebooks.e_o_s = 0;
  281. header_codebooks.granulepos = 0;
  282. ogg_stream_init(&streamout, state->serial);
  283. _commentheader_out(state->vc, state->vendor, &header_comments);
  284. ogg_stream_packetin(&streamout, &header_main);
  285. ogg_stream_packetin(&streamout, &header_comments);
  286. ogg_stream_packetin(&streamout, &header_codebooks);
  287. while((result = ogg_stream_flush(&streamout, &ogout)))
  288. {
  289. if(state->write(ogout.header,1,ogout.header_len, out) !=
  290. (size_t) ogout.header_len)
  291. goto cleanup;
  292. if(state->write(ogout.body,1,ogout.body_len, out) !=
  293. (size_t) ogout.body_len)
  294. goto cleanup;
  295. }
  296. while(_fetch_next_packet(state, &op, &ogin))
  297. {
  298. int size;
  299. size = _blocksize(state, &op);
  300. granpos += size;
  301. if(needflush)
  302. {
  303. if(ogg_stream_flush(&streamout, &ogout))
  304. {
  305. if(state->write(ogout.header,1,ogout.header_len,
  306. out) != (size_t) ogout.header_len)
  307. goto cleanup;
  308. if(state->write(ogout.body,1,ogout.body_len,
  309. out) != (size_t) ogout.body_len)
  310. goto cleanup;
  311. }
  312. }
  313. else if(needout)
  314. {
  315. if(ogg_stream_pageout(&streamout, &ogout))
  316. {
  317. if(state->write(ogout.header,1,ogout.header_len,
  318. out) != (size_t) ogout.header_len)
  319. goto cleanup;
  320. if(state->write(ogout.body,1,ogout.body_len,
  321. out) != (size_t) ogout.body_len)
  322. goto cleanup;
  323. }
  324. }
  325. needflush=needout=0;
  326. if(op.granulepos == -1)
  327. {
  328. op.granulepos = granpos;
  329. ogg_stream_packetin(&streamout, &op);
  330. }
  331. else /* granulepos is set, validly. Use it, and force a flush to
  332. account for shortened blocks (vcut) when appropriate */
  333. {
  334. if(granpos > op.granulepos)
  335. {
  336. granpos = op.granulepos;
  337. ogg_stream_packetin(&streamout, &op);
  338. needflush=1;
  339. }
  340. else
  341. {
  342. ogg_stream_packetin(&streamout, &op);
  343. needout=1;
  344. }
  345. }
  346. }
  347. streamout.e_o_s = 1;
  348. while(ogg_stream_flush(&streamout, &ogout))
  349. {
  350. if(state->write(ogout.header,1,ogout.header_len,
  351. out) != (size_t) ogout.header_len)
  352. goto cleanup;
  353. if(state->write(ogout.body,1,ogout.body_len,
  354. out) != (size_t) ogout.body_len)
  355. goto cleanup;
  356. }
  357. /* FIXME: freeing this here probably leaks memory */
  358. /* Done with this, now */
  359. vorbis_info_clear(&state->vi);
  360. if (state->extrapage)
  361. {
  362. if(state->write(ogin.header,1,ogin.header_len,
  363. out) != (size_t) ogin.header_len)
  364. goto cleanup;
  365. if (state->write(ogin.body,1,ogin.body_len, out) !=
  366. (size_t) ogin.body_len)
  367. goto cleanup;
  368. }
  369. state->eosin=0; /* clear it, because not all paths to here do */
  370. while(!state->eosin) /* We reached eos, not eof */
  371. {
  372. /* We copy the rest of the stream (other logical streams)
  373. * through, a page at a time. */
  374. while(1)
  375. {
  376. result = ogg_sync_pageout(state->oy, &ogout);
  377. if(result==0) break;
  378. if(result<0)
  379. state->lasterror = "Corrupt or missing data, continuing...";
  380. else
  381. {
  382. /* Don't bother going through the rest, we can just
  383. * write the page out now */
  384. if(state->write(ogout.header,1,ogout.header_len,
  385. out) != (size_t) ogout.header_len)
  386. goto cleanup;
  387. if(state->write(ogout.body,1,ogout.body_len, out) !=
  388. (size_t) ogout.body_len)
  389. goto cleanup;
  390. }
  391. }
  392. buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
  393. bytes = state->read(buffer,1, CHUNKSIZE, state->in);
  394. ogg_sync_wrote(state->oy, bytes);
  395. if(bytes == 0)
  396. {
  397. state->eosin = 1;
  398. break;
  399. }
  400. }
  401. cleanup:
  402. ogg_stream_clear(&streamout);
  403. ogg_packet_clear(&header_comments);
  404. free(state->mainbuf);
  405. free(state->bookbuf);
  406. vcedit_clear_internals(state);
  407. if(!state->eosin)
  408. {
  409. state->lasterror =
  410. "Error writing stream to output. "
  411. "Output stream may be corrupted or truncated.";
  412. return -1;
  413. }
  414. return 0;
  415. }