/src/update.c

https://code.google.com/ · C · 367 lines · 260 code · 42 blank · 65 comment · 87 complexity · ef431a022bbac24918981eb8d611b203 MD5 · raw file

  1. /*
  2. $Id: update.c 231 2011-06-27 13:46:19Z marc.noirot $
  3. FLV Metadata updater
  4. Copyright (C) 2007-2012 Marc Noirot <marc.noirot AT gmail.com>
  5. This file is part of FLVMeta.
  6. FLVMeta is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2 of the License, or
  9. (at your option) any later version.
  10. FLVMeta is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with FLVMeta; if not, write to the Free Software
  16. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. */
  18. #include "flvmeta.h"
  19. #include "flv.h"
  20. #include "amf.h"
  21. #include "dump.h"
  22. #include "info.h"
  23. #include "update.h"
  24. #include "util.h"
  25. #include <stdio.h>
  26. #include <string.h>
  27. #include <time.h>
  28. #define COPY_BUFFER_SIZE 4096
  29. #ifdef WIN32
  30. # include "win32_tmpfile.h"
  31. # define flvmeta_tmpfile win32_tmpfile
  32. #else /* WIN32 */
  33. # define flvmeta_tmpfile tmpfile
  34. #endif /* WIN32 */
  35. /*
  36. Write the flv output file
  37. */
  38. static int write_flv(flv_stream * flv_in, FILE * flv_out, const flv_info * info, const flv_metadata * meta, const flvmeta_opts * opts) {
  39. uint32_be size;
  40. uint32 on_metadata_name_size;
  41. uint32 on_metadata_size;
  42. uint32 prev_timestamp_video;
  43. uint32 prev_timestamp_audio;
  44. uint32 prev_timestamp_meta;
  45. uint8 timestamp_extended_video;
  46. uint8 timestamp_extended_audio;
  47. uint8 timestamp_extended_meta;
  48. byte * copy_buffer;
  49. flv_tag ft, omft;
  50. int have_on_last_second;
  51. if (opts->verbose) {
  52. fprintf(stdout, "Writing %s...\n", opts->output_file);
  53. }
  54. /* write the flv header */
  55. if (flv_write_header(flv_out, &info->header) != 1) {
  56. return ERROR_WRITE;
  57. }
  58. /* first "previous tag size" */
  59. size = swap_uint32(0);
  60. if (fwrite(&size, sizeof(uint32_be), 1, flv_out) != 1) {
  61. return ERROR_WRITE;
  62. }
  63. /* create the onMetaData tag */
  64. on_metadata_name_size = (uint32)amf_data_size(meta->on_metadata_name);
  65. on_metadata_size = (uint32)amf_data_size(meta->on_metadata);
  66. omft.type = FLV_TAG_TYPE_META;
  67. omft.body_length = uint32_to_uint24_be(on_metadata_name_size + on_metadata_size);
  68. flv_tag_set_timestamp(&omft, 0);
  69. omft.stream_id = uint32_to_uint24_be(0);
  70. /* write the computed onMetaData tag first if it doesn't exist in the input file */
  71. if (info->on_metadata_size == 0) {
  72. if (flv_write_tag(flv_out, &omft) != 1
  73. || amf_data_file_write(meta->on_metadata_name, flv_out) < on_metadata_name_size
  74. || amf_data_file_write(meta->on_metadata, flv_out) < on_metadata_size) {
  75. return ERROR_WRITE;
  76. }
  77. /* previous tag size */
  78. size = swap_uint32(FLV_TAG_SIZE + on_metadata_name_size + on_metadata_size);
  79. if (fwrite(&size, sizeof(uint32_be), 1, flv_out) != 1) {
  80. return ERROR_WRITE;
  81. }
  82. }
  83. /* extended timestamp initialization */
  84. prev_timestamp_video = 0;
  85. prev_timestamp_audio = 0;
  86. prev_timestamp_meta = 0;
  87. timestamp_extended_video = 0;
  88. timestamp_extended_audio = 0;
  89. timestamp_extended_meta = 0;
  90. /* copy the tags verbatim */
  91. flv_reset(flv_in);
  92. copy_buffer = (byte *)malloc(info->biggest_tag_body_size + FLV_TAG_SIZE);
  93. have_on_last_second = 0;
  94. while (flv_read_tag(flv_in, &ft) == FLV_OK) {
  95. file_offset_t offset;
  96. uint32 body_length;
  97. uint32 timestamp;
  98. offset = flv_get_current_tag_offset(flv_in);
  99. body_length = flv_tag_get_body_length(ft);
  100. timestamp = flv_tag_get_timestamp(ft);
  101. /* extended timestamp fixing */
  102. if (ft.type == FLV_TAG_TYPE_META) {
  103. if (timestamp < prev_timestamp_meta
  104. && prev_timestamp_meta - timestamp > 0xF00000) {
  105. ++timestamp_extended_meta;
  106. }
  107. prev_timestamp_meta = timestamp;
  108. if (timestamp_extended_meta > 0) {
  109. timestamp += timestamp_extended_meta << 24;
  110. }
  111. }
  112. else if (ft.type == FLV_TAG_TYPE_AUDIO) {
  113. if (timestamp < prev_timestamp_audio
  114. && prev_timestamp_audio - timestamp > 0xF00000) {
  115. ++timestamp_extended_audio;
  116. }
  117. prev_timestamp_audio = timestamp;
  118. if (timestamp_extended_audio > 0) {
  119. timestamp += timestamp_extended_audio << 24;
  120. }
  121. }
  122. else if (ft.type == FLV_TAG_TYPE_VIDEO) {
  123. if (timestamp < prev_timestamp_video
  124. && prev_timestamp_video - timestamp > 0xF00000) {
  125. ++timestamp_extended_video;
  126. }
  127. prev_timestamp_video = timestamp;
  128. if (timestamp_extended_video > 0) {
  129. timestamp += timestamp_extended_video << 24;
  130. }
  131. }
  132. /* non-zero starting timestamp handling */
  133. if (opts->reset_timestamps && timestamp > 0) {
  134. timestamp -= info->first_timestamp;
  135. }
  136. flv_tag_set_timestamp(&ft, timestamp);
  137. /* if we're at the offset of the first onMetaData tag in the input file,
  138. we write the one we computed instead, discarding the old one */
  139. if (info->on_metadata_offset == offset) {
  140. if (flv_write_tag(flv_out, &omft) != 1
  141. || amf_data_file_write(meta->on_metadata_name, flv_out) < on_metadata_name_size
  142. || amf_data_file_write(meta->on_metadata, flv_out) < on_metadata_size) {
  143. free(copy_buffer);
  144. return ERROR_WRITE;
  145. }
  146. /* previous tag size */
  147. size = swap_uint32(FLV_TAG_SIZE + on_metadata_name_size + on_metadata_size);
  148. if (fwrite(&size, sizeof(uint32_be), 1, flv_out) != 1) {
  149. free(copy_buffer);
  150. return ERROR_WRITE;
  151. }
  152. }
  153. else {
  154. size_t read_body;
  155. /* insert an onLastSecond metadata tag */
  156. if (opts->insert_onlastsecond && !have_on_last_second && !info->have_on_last_second && (info->last_timestamp - timestamp) <= 1000) {
  157. flv_tag tag;
  158. uint32 on_last_second_name_size = (uint32)amf_data_size(meta->on_last_second_name);
  159. uint32 on_last_second_size = (uint32)amf_data_size(meta->on_last_second);
  160. tag.type = FLV_TAG_TYPE_META;
  161. tag.body_length = uint32_to_uint24_be(on_last_second_name_size + on_last_second_size);
  162. tag.timestamp = ft.timestamp;
  163. tag.timestamp_extended = ft.timestamp_extended;
  164. tag.stream_id = uint32_to_uint24_be(0);
  165. if (flv_write_tag(flv_out, &tag) != 1
  166. || amf_data_file_write(meta->on_last_second_name, flv_out) < on_last_second_name_size
  167. || amf_data_file_write(meta->on_last_second, flv_out) < on_last_second_size) {
  168. free(copy_buffer);
  169. return ERROR_WRITE;
  170. }
  171. /* previous tag size */
  172. size = swap_uint32(FLV_TAG_SIZE + on_last_second_name_size + on_last_second_size);
  173. if (fwrite(&size, sizeof(uint32_be), 1, flv_out) != 1) {
  174. free(copy_buffer);
  175. return ERROR_WRITE;
  176. }
  177. have_on_last_second = 1;
  178. }
  179. /* if the tag is bigger than expected, it means that
  180. it's an unknown tag type. In this case, we only
  181. copy as much data as the copy buffer can contain */
  182. if (body_length > info->biggest_tag_body_size) {
  183. body_length = info->biggest_tag_body_size;
  184. }
  185. /* copy the tag verbatim */
  186. read_body = flv_read_tag_body(flv_in, copy_buffer, body_length);
  187. if (read_body < body_length) {
  188. /* we have reached end of file on an incomplete tag */
  189. if (opts->error_handling == FLVMETA_EXIT_ON_ERROR) {
  190. free(copy_buffer);
  191. return ERROR_EOF;
  192. }
  193. else if (opts->error_handling == FLVMETA_FIX_ERRORS) {
  194. /* the tag is bogus, just omit it,
  195. even though it will make the whole file length
  196. calculation wrong, and the metadata inaccurate */
  197. /* TODO : fix it by handling that problem in the first pass */
  198. free(copy_buffer);
  199. return OK;
  200. }
  201. else if (opts->error_handling == FLVMETA_IGNORE_ERRORS) {
  202. /* just copy the whole tag and exit */
  203. flv_write_tag(flv_out, &ft);
  204. fwrite(copy_buffer, 1, read_body, flv_out);
  205. free(copy_buffer);
  206. size = swap_uint32(FLV_TAG_SIZE + read_body);
  207. fwrite(&size, sizeof(uint32_be), 1, flv_out);
  208. return OK;
  209. }
  210. }
  211. if (flv_write_tag(flv_out, &ft) != 1
  212. || fwrite(copy_buffer, 1, body_length, flv_out) < body_length) {
  213. free(copy_buffer);
  214. return ERROR_WRITE;
  215. }
  216. /* previous tag length */
  217. size = swap_uint32(FLV_TAG_SIZE + body_length);
  218. if (fwrite(&size, sizeof(uint32_be), 1, flv_out) != 1) {
  219. free(copy_buffer);
  220. return ERROR_WRITE;
  221. }
  222. }
  223. }
  224. if (opts->verbose) {
  225. fprintf(stdout, "%s successfully written\n", opts->output_file);
  226. }
  227. free(copy_buffer);
  228. return OK;
  229. }
  230. /* copy a FLV file while adding onMetaData and optionnally onLastSecond events */
  231. int update_metadata(const flvmeta_opts * opts) {
  232. int res, in_place_update;
  233. flv_stream * flv_in;
  234. FILE * flv_out;
  235. flv_info info;
  236. flv_metadata meta;
  237. flv_in = flv_open(opts->input_file);
  238. if (flv_in == NULL) {
  239. return ERROR_OPEN_READ;
  240. }
  241. /*
  242. get all necessary information from the flv file
  243. */
  244. res = get_flv_info(flv_in, &info, opts);
  245. if (res != OK) {
  246. flv_close(flv_in);
  247. amf_data_free(info.keyframes);
  248. return res;
  249. }
  250. compute_metadata(&info, &meta, opts);
  251. /*
  252. open output file
  253. */
  254. /* detect whether we have to overwrite the input file */
  255. if (same_file(opts->input_file, opts->output_file)) {
  256. in_place_update = 1;
  257. flv_out = flvmeta_tmpfile();
  258. }
  259. else {
  260. in_place_update = 0;
  261. flv_out = fopen(opts->output_file, "wb");
  262. }
  263. if (flv_out == NULL) {
  264. flv_close(flv_in);
  265. amf_data_free(meta.on_last_second_name);
  266. amf_data_free(meta.on_last_second);
  267. amf_data_free(meta.on_metadata_name);
  268. amf_data_free(meta.on_metadata);
  269. amf_data_free(info.original_on_metadata);
  270. return ERROR_OPEN_WRITE;
  271. }
  272. /*
  273. write the output file
  274. */
  275. res = write_flv(flv_in, flv_out, &info, &meta, opts);
  276. flv_close(flv_in);
  277. amf_data_free(meta.on_last_second_name);
  278. amf_data_free(meta.on_last_second);
  279. amf_data_free(meta.on_metadata_name);
  280. amf_data_free(info.original_on_metadata);
  281. /* copy data into the original file if needed */
  282. if (in_place_update == 1) {
  283. FILE * flv_out_real;
  284. size_t bytes_read;
  285. byte copy_buffer[COPY_BUFFER_SIZE];
  286. flv_out_real = fopen(opts->output_file, "wb");
  287. if (flv_out_real == NULL) {
  288. amf_data_free(meta.on_metadata);
  289. return ERROR_OPEN_WRITE;
  290. }
  291. /* copy temporary file contents into the final file */
  292. lfs_fseek(flv_out, 0, SEEK_SET);
  293. while (!feof(flv_out)) {
  294. bytes_read = fread(copy_buffer, sizeof(byte), COPY_BUFFER_SIZE, flv_out);
  295. if (bytes_read > 0) {
  296. if (fwrite(copy_buffer, sizeof(byte), bytes_read, flv_out_real) < bytes_read) {
  297. fclose(flv_out_real);
  298. fclose(flv_out);
  299. amf_data_free(meta.on_metadata);
  300. return ERROR_WRITE;
  301. }
  302. }
  303. else {
  304. fclose(flv_out_real);
  305. fclose(flv_out);
  306. amf_data_free(meta.on_metadata);
  307. return ERROR_WRITE;
  308. }
  309. }
  310. fclose(flv_out_real);
  311. }
  312. fclose(flv_out);
  313. /* dump computed metadata if we have to */
  314. if (opts->dump_metadata == 1) {
  315. dump_amf_data(meta.on_metadata, opts);
  316. }
  317. amf_data_free(meta.on_metadata);
  318. return res;
  319. }