You've already forked FFmpeg
							
							
				mirror of
				https://github.com/FFmpeg/FFmpeg.git
				synced 2025-10-30 23:18:11 +02:00 
			
		
		
		
	lavu: add public timecode API.
This commit is contained in:
		
				
					committed by
					
						 Clément Bœsch
						Clément Bœsch
					
				
			
			
				
	
			
			
			
						parent
						
							b18ebcbe83
						
					
				
				
					commit
					0eaa123b34
				
			| @@ -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. | ||||
|  | ||||
|   | ||||
| @@ -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                                                          \ | ||||
|  | ||||
|   | ||||
| @@ -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, \ | ||||
|   | ||||
							
								
								
									
										194
									
								
								libavutil/timecode.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								libavutil/timecode.c
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||
| } | ||||
							
								
								
									
										138
									
								
								libavutil/timecode.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								libavutil/timecode.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 */ | ||||
		Reference in New Issue
	
	Block a user