diff options
author | Clément Bœsch <clement.boesch@smartjog.com> | 2012-01-16 11:20:46 +0100 |
---|---|---|
committer | Clément Bœsch <ubitux@gmail.com> | 2012-02-02 14:30:28 +0100 |
commit | 0eaa123b3423140c6103746b5f9f71129a25cd38 (patch) | |
tree | 58b2e161ca021d0145c71df451274a79eca2103a | |
parent | b18ebcbe831ed63c6b458e868e4fd9d6492e974c (diff) | |
download | ffmpeg-0eaa123b3423140c6103746b5f9f71129a25cd38.tar.gz |
lavu: add public timecode API.
-rw-r--r-- | doc/APIchanges | 3 | ||||
-rw-r--r-- | libavutil/Makefile | 2 | ||||
-rw-r--r-- | libavutil/avutil.h | 2 | ||||
-rw-r--r-- | libavutil/timecode.c | 194 | ||||
-rw-r--r-- | libavutil/timecode.h | 138 |
5 files changed, 338 insertions, 1 deletions
diff --git a/doc/APIchanges b/doc/APIchanges index 507a9381c3..bc68cb0b32 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -13,6 +13,9 @@ libavutil: 2011-04-18 API changes, most recent first: +2012-02-02 - xxxxxxx - lavu 51.37.100 + Add public timecode helpers. + 2012-01-24 - xxxxxxx - lavfi 2.60.100 Add avfilter_graph_dump. diff --git a/libavutil/Makefile b/libavutil/Makefile index 2cd763c1e7..5034f5a113 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -37,6 +37,7 @@ HEADERS = adler32.h \ rational.h \ samplefmt.h \ sha.h \ + timecode.h \ BUILT_HEADERS = avconfig.h @@ -71,6 +72,7 @@ OBJS = adler32.o \ rc4.o \ samplefmt.o \ sha.o \ + timecode.o \ tree.o \ utils.o \ diff --git a/libavutil/avutil.h b/libavutil/avutil.h index d5b35b9eb9..5d574e8851 100644 --- a/libavutil/avutil.h +++ b/libavutil/avutil.h @@ -154,7 +154,7 @@ */ #define LIBAVUTIL_VERSION_MAJOR 51 -#define LIBAVUTIL_VERSION_MINOR 36 +#define LIBAVUTIL_VERSION_MINOR 37 #define LIBAVUTIL_VERSION_MICRO 100 #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ diff --git a/libavutil/timecode.c b/libavutil/timecode.c new file mode 100644 index 0000000000..db3c818d7b --- /dev/null +++ b/libavutil/timecode.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2006 Smartjog S.A.S, Baptiste Coudurier <baptiste.coudurier@gmail.com> + * Copyright (c) 2011-2012 Smartjog S.A.S, Clément Bœsch <clement.boesch@smartjog.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Timecode helpers + * A few references: + * https://en.wikipedia.org/wiki/SMPTE_time_code + * http://www.dropframetimecode.org + */ + +#include <stdio.h> +#include "timecode.h" +#include "log.h" +#include "error.h" + +int av_timecode_adjust_ntsc_framenum(int framenum) +{ + /* only works for NTSC 29.97 */ + int d = framenum / 17982; + int m = framenum % 17982; + //if (m < 2) m += 2; /* not needed since -2,-1 / 1798 in C returns 0 */ + return framenum + 18 * d + 2 * ((m - 2) / 1798); +} + +uint32_t av_timecode_get_smpte_from_framenum(const AVTimecode *tc, int framenum) +{ + unsigned fps = tc->fps; + int drop = !!(tc->flags & AV_TIMECODE_FLAG_DROPFRAME); + int hh, mm, ss, ff; + + framenum += tc->start; + if (drop) + framenum = av_timecode_adjust_ntsc_framenum(framenum); + ff = framenum % fps; + ss = framenum / fps % 60; + mm = framenum / (fps*60) % 60; + hh = framenum / (fps*3600) % 24; + return 0 << 31 | // color frame flag (0: unsync mode, 1: sync mode) + drop << 30 | // drop frame flag (0: non drop, 1: drop) + (ff / 10) << 28 | // tens of frames + (ff % 10) << 24 | // units of frames + 0 << 23 | // PC (NTSC) or BGF0 (PAL) + (ss / 10) << 20 | // tens of seconds + (ss % 10) << 16 | // units of seconds + 0 << 15 | // BGF0 (NTSC) or BGF2 (PAL) + (mm / 10) << 12 | // tens of minutes + (mm % 10) << 8 | // units of minutes + 0 << 7 | // BGF2 (NTSC) or PC (PAL) + 0 << 6 | // BGF1 + (hh / 10) << 4 | // tens of hours + (hh % 10); // units of hours +} + +char *av_timecode_make_string(const AVTimecode *tc, char *buf, int framenum) +{ + int fps = tc->fps; + int drop = tc->flags & AV_TIMECODE_FLAG_DROPFRAME; + int hh, mm, ss, ff, neg = 0; + + framenum += tc->start; + if (drop) + framenum = av_timecode_adjust_ntsc_framenum(framenum); + if (framenum < 0) { + framenum = -framenum; + neg = tc->flags & AV_TIMECODE_FLAG_ALLOWNEGATIVE; + } + ff = framenum % fps; + ss = framenum / fps % 60; + mm = framenum / (fps*60) % 60; + hh = framenum / (fps*3600); + if (tc->flags & AV_TIMECODE_FLAG_24HOURSMAX) + hh = hh % 24; + snprintf(buf, AV_TIMECODE_STR_SIZE, "%s%02d:%02d:%02d%c%02d", + neg ? "-" : "", + hh, mm, ss, drop ? ';' : ':', ff); + return buf; +} + +static unsigned bcd2uint(uint8_t bcd) +{ + unsigned low = bcd & 0xf; + unsigned high = bcd >> 4; + if (low > 9 || high > 9) + return 0; + return low + 10*high; +} + +char *av_timecode_make_smpte_tc_string(char *buf, uint32_t tcsmpte, int prevent_df) +{ + unsigned hh = bcd2uint(tcsmpte & 0x3f); // 6-bit hours + unsigned mm = bcd2uint(tcsmpte>>8 & 0x7f); // 7-bit minutes + unsigned ss = bcd2uint(tcsmpte>>16 & 0x7f); // 7-bit seconds + unsigned ff = bcd2uint(tcsmpte>>24 & 0x3f); // 6-bit frames + unsigned drop = tcsmpte & 1<<30 && !prevent_df; // 1-bit drop if not arbitrary bit + snprintf(buf, AV_TIMECODE_STR_SIZE, "%02u:%02u:%02u%c%02u", + hh, mm, ss, drop ? ';' : ':', ff); + return buf; +} + +char *av_timecode_make_mpeg_tc_string(char *buf, uint32_t tc25bit) +{ + snprintf(buf, AV_TIMECODE_STR_SIZE, "%02u:%02u:%02u%c%02u", + tc25bit>>19 & 0x1f, // 5-bit hours + tc25bit>>13 & 0x3f, // 6-bit minutes + tc25bit>>6 & 0x3f, // 6-bit seconds + tc25bit & 1<<24 ? ';' : ':', // 1-bit drop flag + tc25bit & 0x3f); // 6-bit frames + return buf; +} + +static int check_timecode(void *log_ctx, AVTimecode *tc) +{ + if (tc->fps <= 0) { + av_log(log_ctx, AV_LOG_ERROR, "Timecode frame rate must be specified\n"); + return AVERROR(EINVAL); + } + if ((tc->flags & AV_TIMECODE_FLAG_DROPFRAME) && tc->fps != 30) { + av_log(log_ctx, AV_LOG_ERROR, "Drop frame is only allowed with 30000/1001 FPS\n"); + return AVERROR(EINVAL); + } + switch (tc->fps) { + case 24: + case 25: + case 30: return 0; + + default: + av_log(log_ctx, AV_LOG_ERROR, "Timecode frame rate not supported\n"); + return AVERROR_PATCHWELCOME; + } +} + +static int fps_from_frame_rate(AVRational rate) +{ + if (!rate.den || !rate.num) + return -1; + return (rate.num + rate.den/2) / rate.den; +} + +int av_timecode_init(AVTimecode *tc, AVRational rate, int flags, int frame_start, void *log_ctx) +{ + memset(tc, 0, sizeof(*tc)); + tc->start = frame_start; + tc->flags = flags; + tc->rate = rate; + tc->fps = fps_from_frame_rate(rate); + return check_timecode(log_ctx, tc); +} + +int av_timecode_init_from_string(AVTimecode *tc, AVRational rate, const char *str, void *log_ctx) +{ + char c; + int hh, mm, ss, ff, ret; + + if (sscanf(str, "%d:%d:%d%c%d", &hh, &mm, &ss, &c, &ff) != 5) { + av_log(log_ctx, AV_LOG_ERROR, "Unable to parse timecode, " + "syntax: hh:mm:ss[:;.]ff\n"); + return AVERROR_INVALIDDATA; + } + + memset(tc, 0, sizeof(*tc)); + tc->flags = c != ':' ? AV_TIMECODE_FLAG_DROPFRAME : 0; // drop if ';', '.', ... + tc->rate = rate; + tc->fps = fps_from_frame_rate(rate); + + ret = check_timecode(log_ctx, tc); + if (ret < 0) + return ret; + + tc->start = (hh*3600 + mm*60 + ss) * tc->fps + ff; + if (tc->flags & AV_TIMECODE_FLAG_DROPFRAME) { /* adjust frame number */ + int tmins = 60*hh + mm; + tc->start -= 2 * (tmins - tmins/10); + } + return 0; +} diff --git a/libavutil/timecode.h b/libavutil/timecode.h new file mode 100644 index 0000000000..41e56d6bfa --- /dev/null +++ b/libavutil/timecode.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2006 Smartjog S.A.S, Baptiste Coudurier <baptiste.coudurier@gmail.com> + * Copyright (c) 2011-2012 Smartjog S.A.S, Clément Bœsch <clement.boesch@smartjog.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Timecode helpers header + */ + +#ifndef AVUTIL_TIMECODE_H +#define AVUTIL_TIMECODE_H + +#include <stdint.h> +#include "rational.h" + +#define AV_TIMECODE_STR_SIZE 16 + +#define AV_TIMECODE_OPTION(ctx, string_field, flags) \ + "timecode", "set timecode value following hh:mm:ss[:;.]ff format, " \ + "use ';' or '.' before frame number for drop frame", \ + offsetof(ctx, string_field), \ + AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, flags + +enum AVTimecodeFlag { + AV_TIMECODE_FLAG_DROPFRAME = 1<<0, ///< timecode is drop frame + AV_TIMECODE_FLAG_24HOURSMAX = 1<<1, ///< timecode wraps after 24 hours + AV_TIMECODE_FLAG_ALLOWNEGATIVE = 1<<2, ///< negative time values are allowed +}; + +typedef struct { + int start; ///< timecode frame start (first base frame number) + uint32_t flags; ///< flags such as drop frame, +24 hours support, ... + AVRational rate; ///< frame rate in rational form + unsigned fps; ///< frame per second; must be consistent with the rate field +} AVTimecode; + +/** + * Adjust frame number for NTSC drop frame time code. + * + * @param framenum frame number to adjust + * @return adjusted frame number + * @warning adjustment is only valid in NTSC 29.97 + */ +int av_timecode_adjust_ntsc_framenum(int framenum); + +/** + * Convert frame number to SMPTE 12M binary representation. + * + * @param tc timecode data correctly initialized + * @param framenum frame number + * @return the SMPTE binary representation + * + * @note Frame number adjustment is automatically done in case of drop timecode, + * you do NOT have to call av_timecode_adjust_ntsc_framenum(). + * @note The frame number is relative to tc->start. + * @note Color frame (CF), binary group flags (BGF) and biphase mark polarity + * correction (PC) bits are set to zero. + */ +uint32_t av_timecode_get_smpte_from_framenum(const AVTimecode *tc, int framenum); + +/** + * Load timecode string in buf. + * + * @param buf destination buffer, must be at least AV_TIMECODE_STR_SIZE long + * @param tc timecode data correctly initialized + * @param framenum frame number + * @return the buf parameter + * + * @note Timecode representation can be a negative timecode and have more than + * 24 hours, but will only be honored if the flags are correctly set. + * @note The frame number is relative to tc->start. + */ +char *av_timecode_make_string(const AVTimecode *tc, char *buf, int framenum); + +/** + * Get the timecode string from the SMPTE timecode format. + * + * @param buf destination buffer, must be at least AV_TIMECODE_STR_SIZE long + * @param tcsmpte the 32-bit SMPTE timecode + * @param prevent_df prevent the use of a drop flag when it is known the DF bit + * is arbitrary + * @return the buf parameter + */ +char *av_timecode_make_smpte_tc_string(char *buf, uint32_t tcsmpte, int prevent_df); + +/** + * Get the timecode string from the 25-bit timecode format (MPEG GOP format). + * + * @param buf destination buffer, must be at least AV_TIMECODE_STR_SIZE long + * @param tc25bit the 25-bits timecode + * @return the buf parameter + */ +char *av_timecode_make_mpeg_tc_string(char *buf, uint32_t tc25bit); + +/** + * Init a timecode struct with the passed parameters. + * + * @param log_ctx a pointer to an arbitrary struct of which the first field + * is a pointer to an AVClass struct (used for av_log) + * @param tc pointer to an allocated AVTimecode + * @param rate frame rate in rational form + * @param flags miscellaneous flags such as drop frame, +24 hours, ... + * (see AVTimecodeFlag) + * @param frame_start the first frame number + * @return 0 on success, AVERROR otherwise + */ +int av_timecode_init(AVTimecode *tc, AVRational rate, int flags, int frame_start, void *log_ctx); + +/** + * Parse timecode representation (hh:mm:ss[:;.]ff). + * + * @param log_ctx a pointer to an arbitrary struct of which the first field is a + * pointer to an AVClass struct (used for av_log). + * @param tc pointer to an allocated AVTimecode + * @param rate frame rate in rational form + * @param str timecode string which will determine the frame start + * @return 0 on success, AVERROR otherwise + */ +int av_timecode_init_from_string(AVTimecode *tc, AVRational rate, const char *str, void *log_ctx); + +#endif /* AVUTIL_TIMECODE_H */ |