Skip to content
Draft
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/upipe/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ pkginclude_HEADERS = \
urefcount.h \
urefcount_helper.h \
uref_attr.h \
uref_attr_s12m.h \
uref_block_flow.h \
uref_block.h \
uref_clock.h \
Expand Down
4 changes: 3 additions & 1 deletion include/upipe/udict.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@ enum udict_type {
/** p.cea_708 */
UDICT_TYPE_PIC_CEA_708,
/** p.bar_data */
UDICT_TYPE_PIC_BAR_DATA
UDICT_TYPE_PIC_BAR_DATA,
/** p.s12m */
UDICT_TYPE_PIC_S12M,
};

/** @This defines standard commands which udict modules may implement. */
Expand Down
250 changes: 250 additions & 0 deletions include/upipe/uref_attr_s12m.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
/*
* Copyright (C) 2024 Open Broadcast Systems Ltd
*
* Authors: James Darnley
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject
* to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/** @file
* @short Upipe uref s12m attributes handling
*
* Helper functions to handle the s12m timecode attributes. See the SMPTE
* standards 12M-1 and 314M for more details.
*/

#ifndef _UPIPE_UREF_ATTR_S12M_H_
/** @hidden */
#define _UPIPE_UREF_ATTR_S12M_H_
#ifdef __cplusplus
extern "C" {
#endif

#include "upipe/uref_pic.h"

#include <stdbool.h>
#include <stdint.h>

/** @this returns a uint32_t read from possibly unaligned data
*
* Timecode packs and the count are native endian values.
*/
static inline uint32_t uref_attr_s12m_read(const uint8_t *data)
{
/* TODO: big endian */
return data[0] | data[1] << 8 | data[2] << 16 | (uint32_t)data[3] << 24;
}

/** @This returns true if the data and size given repesents a valid s12m
* attribute.
*/
static inline bool uref_attr_s12m_check(const uint8_t *data, const size_t size)
{
/* Size must be at least 4 for a single uint32_t. */
if (size < 4)
return false;
const uint32_t count = uref_attr_s12m_read(data);
/* The count should be followed by that number of uint32_t. */
if (size < sizeof(uint32_t) * (count+1))
return false;
return true;
}

/** @This returns true if the integer values are permissible in a timecode.
*/
static inline bool uref_attr_s12m_validate_integers(const uint32_t hours,
const uint32_t minutes, const uint32_t seconds, const uint32_t frames)
{
if (hours > 23)
return false;
if (minutes > 59)
return false;
if (seconds > 59)
return false;
if (frames > 29)
return false;
return true;
}

/** @This returns true if the decimal values are permissible in a timecode.
*/
static inline bool uref_attr_s12m_validate_decimals(const uint32_t hours_10s,
const uint32_t hours_1s, const uint32_t minutes_10s, const uint32_t minutes_1s,
const uint32_t seconds_10s, const uint32_t seconds_1s, const uint32_t frames_10s,
const uint32_t frames_1s)
{
return uref_attr_s12m_validate_integers(10*hours_10s + hours_1s,
10*minutes_10s + minutes_1s, 10*seconds_10s + seconds_1s,
10*frames_10s + frames_1s);
}

/** @This returns a new timecode pack from the integer components and the drop
* frame flag. Does not validate values.
*
* @param hours 0-23
* @param minutes 0-59
* @param seconds 0-59
* @param frames 0 to framerate
* @param drop true or false
* @return timecode pack
*/

static inline uint32_t uref_attr_s12m_from_integers(const uint32_t hours,
const uint32_t minutes, const uint32_t seconds, const uint32_t frames,
const uint32_t drop)
{
return drop << 30
| hours % 10
| hours / 10 << 4
| minutes % 10 << 8
| minutes / 10 << 12
| seconds % 10 << 16
| seconds / 10 << 20
| frames % 10 << 24
| frames / 10 << 28;
}

/** @This splits a timecode pack into the integer components and the drop frame
* flag. Does not validate.
* values.
*
* @param timecode timecode pack
* @param hours pointer
* @param minutes pointer
* @param seconds pointer
* @param frames pointer
* @param drop pointer
*/

static inline void uref_attr_s12m_to_integers(const uint32_t timecode,
uint8_t *hours, uint8_t *minutes, uint8_t *seconds, uint8_t *frames,
bool *drop)
{
*hours = 10 * (timecode >> 4 & 0x3) + (timecode & 0xf);
*minutes = 10 * (timecode >> 12 & 0x7) + (timecode >> 8 & 0xf);
*seconds = 10 * (timecode >> 20 & 0x7) + (timecode >> 16 & 0xf);
*frames = 10 * (timecode >> 28 & 0x3) + (timecode >> 24 & 0xf);
*drop = (timecode & 1<<30) == 1<<30;
}

/** @This returns a new timecode pack from the decimal components and the drop
* frame flag. Does not validate values.
*
* @param hours_10s 0-2
* @param hours_1s 0-9
* @param minutes_10s 0-5
* @param minutes_1s 0-9
* @param seconds_10s 0-5
* @param seconds_1s 0-9
* @param frames_10s 0 to tens of framerate
* @param frames_1s 0-9
* @param drop true or false
* @return timecode pack
*/

static inline uint32_t uref_attr_s12m_from_decimals(const uint32_t hours_10s,
const uint32_t hours_1s, const uint32_t minutes_10s, const uint32_t minutes_1s,
const uint32_t seconds_10s, const uint32_t seconds_1s, const uint32_t frames_10s,
const uint32_t frames_1s, const uint32_t drop)
{
return drop << 30
| hours_1s
| hours_10s << 4
| minutes_1s << 8
| minutes_10s << 12
| seconds_1s << 16
| seconds_10s << 20
| frames_1s << 24
| frames_10s << 28;
}

/** @This splits a timecode pack into the decimal components and the drop frame
* flag. Does not validate values.
*
* @pack timecode timecode pack
* @param hours_10s pointer
* @param hours_1s pointer
* @param minutes_10s pointer
* @param minutes_1s pointer
* @param seconds_10s pointer
* @param seconds_1s pointer
* @param frames_10s pointer
* @param frames_1s pointer
* @param drop pointer
*/

static inline void uref_attr_s12m_to_decimals(const uint32_t timecode,
uint8_t *hours_10s, uint8_t *hours_1s, uint8_t *minutes_10s, uint8_t *minutes_1s,
uint8_t *seconds_10s, uint8_t *seconds_1s, uint8_t *frames_10s, uint8_t *frames_1s,
bool *drop)
{
*hours_10s = timecode >> 4 & 0x3;
*hours_1s = timecode & 0xf;
*minutes_10s = timecode >> 12 & 0x7;
*minutes_1s = timecode >> 8 & 0xf;
*seconds_10s = timecode >> 20 & 0x7;
*seconds_1s = timecode >> 16 & 0xf;
*frames_10s = timecode >> 28 & 0x3;
*frames_1s = timecode >> 24 & 0xf;
*drop = (timecode & 1 << 30) == 1 << 30;
}

/** @This gets the field flag in a timecode pack
*
* @param timecode timecode pack
* @param is_pal get the flag in the pal location instead of ntsc location
* @return the flag as a boolean
*/

static inline bool uref_attr_s12m_get_field_flag(const uint32_t timecode, const bool is_pal)
{
if (is_pal)
return (timecode & 1 << 7) == 1 << 7;
else
return (timecode & 1 << 23) == 1 << 23;
}

/** @This sets the field flag in a timecode pack
*
* @param timecode old timecode pack
* @param is_pal set the flag in the pal location instead of ntsc location
* @param field field flag as a boolean
* @return new timecode pack
*/

static inline uint32_t uref_attr_s12m_set_field_flag(const uint32_t timecode, const bool is_pal, const bool field)
{
if (is_pal) {
if (field)
return timecode | 1 << 7;
else
return timecode & ~(1 << 7);
} else {
if (field)
return timecode | 1 << 23;
else
return timecode & ~(1 << 23);
}
}

#ifdef __cplusplus
}
#endif
#endif
1 change: 1 addition & 0 deletions include/upipe/uref_pic.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ UREF_ATTR_VOID_SH(pic, tff, UDICT_TYPE_PIC_TFF, top field first)
UREF_ATTR_SMALL_UNSIGNED_SH(pic, afd, UDICT_TYPE_PIC_AFD, active format description)
UREF_ATTR_OPAQUE_SH(pic, cea_708, UDICT_TYPE_PIC_CEA_708, cea-708 captions)
UREF_ATTR_OPAQUE_SH(pic, bar_data, UDICT_TYPE_PIC_BAR_DATA, afd bar data)
UREF_ATTR_OPAQUE_SH(pic, s12m, UDICT_TYPE_PIC_S12M, SMPTE 12M timecode compatible with ffmpeg AV_FRAME_DATA_S12M_TIMECODE)
UREF_ATTR_UNSIGNED(pic, original_height, "p.original_height", original picture height before chunking)
UREF_ATTR_VOID(pic, c_not_y, "p.c_not_y", whether ancillary data is found in chroma space)

Expand Down
8 changes: 8 additions & 0 deletions lib/upipe-av/upipe_avcodec_decode.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "upipe/uclock.h"
#include "upipe/ubuf.h"
#include "upipe/uref.h"
#include "upipe/uref_attr_s12m.h"
#include "upipe/uref_pic.h"
#include "upipe/uref_flow.h"
#include "upipe/uref_pic_flow.h"
Expand Down Expand Up @@ -1324,6 +1325,13 @@ static void upipe_avcdec_output_pic(struct upipe *upipe, struct upump **upump_p)
if (side_data)
uref_pic_set_cea_708(uref, side_data->data, side_data->size);

#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(56, 20, 100)
side_data = av_frame_get_side_data(frame, AV_FRAME_DATA_S12M_TIMECODE);
if (side_data && uref_attr_s12m_check(side_data->data, side_data->size)) {
uref_pic_set_s12m(uref, side_data->data, side_data->size);
}
#endif

/* various time-related attributes */
upipe_avcdec_set_time_attributes(upipe, uref);

Expand Down
Loading