[FFmpeg-devel] [PATCH 3/3] h264/pic_timing: support multiple timecodes
joshdk at ob-encoder.com
joshdk at ob-encoder.com
Tue Oct 9 16:32:04 EEST 2018
From: Josh de Kock <joshdk at obe.tv>
---
libavcodec/h264_sei.c | 21 ++++++++------
libavcodec/h264_sei.h | 28 +++++++++++++------
libavcodec/h264_slice.c | 59 ++++++++++++++++++++++-----------------
libavfilter/vf_showinfo.c | 10 +++++++
4 files changed, 75 insertions(+), 43 deletions(-)
diff --git a/libavcodec/h264_sei.c b/libavcodec/h264_sei.c
index 275224eabe..d4eb9c0dab 100644
--- a/libavcodec/h264_sei.c
+++ b/libavcodec/h264_sei.c
@@ -84,8 +84,10 @@ static int decode_picture_timing(H264SEIPictureTiming *h, GetBitContext *gb,
return AVERROR_INVALIDDATA;
num_clock_ts = sei_num_clock_ts_table[h->pic_struct];
+ h->timecode_cnt = 0;
for (i = 0; i < num_clock_ts; i++) {
if (get_bits(gb, 1)) { /* clock_timestamp_flag */
+ H264SEITimeCode *tc = &h->timecode[h->timecode_cnt++];
unsigned int full_timestamp_flag;
unsigned int counting_type, cnt_dropped_flag;
h->ct_type |= 1 << get_bits(gb, 2);
@@ -95,20 +97,21 @@ static int decode_picture_timing(H264SEIPictureTiming *h, GetBitContext *gb,
skip_bits(gb, 1); /* discontinuity_flag */
cnt_dropped_flag = get_bits(gb, 1); /* cnt_dropped_flag */
if (cnt_dropped_flag && counting_type > 1 && counting_type < 7)
- h->tc_dropframe = 1;
- h->tc_frames = get_bits(gb, 8); /* n_frames */
+ tc->dropframe = 1;
+ tc->frame = get_bits(gb, 8); /* n_frames */
if (full_timestamp_flag) {
- h->fulltc_received = 1;
- h->tc_seconds = get_bits(gb, 6); /* seconds_value 0..59 */
- h->tc_minutes = get_bits(gb, 6); /* minutes_value 0..59 */
- h->tc_hours = get_bits(gb, 5); /* hours_value 0..23 */
+ tc->full = 1;
+ tc->seconds = get_bits(gb, 6); /* seconds_value 0..59 */
+ tc->minutes = get_bits(gb, 6); /* minutes_value 0..59 */
+ tc->hours = get_bits(gb, 5); /* hours_value 0..23 */
} else {
+ tc->seconds = tc->minutes = tc->hours = tc->full = 0;
if (get_bits(gb, 1)) { /* seconds_flag */
- h->tc_seconds = get_bits(gb, 6);
+ tc->seconds = get_bits(gb, 6);
if (get_bits(gb, 1)) { /* minutes_flag */
- h->tc_minutes = get_bits(gb, 6);
+ tc->minutes = get_bits(gb, 6);
if (get_bits(gb, 1)) /* hours_flag */
- h->tc_minutes = get_bits(gb, 5);
+ tc->hours = get_bits(gb, 5);
}
}
}
diff --git a/libavcodec/h264_sei.h b/libavcodec/h264_sei.h
index 3b8806be0a..a75c3aa175 100644
--- a/libavcodec/h264_sei.h
+++ b/libavcodec/h264_sei.h
@@ -67,6 +67,17 @@ typedef enum {
H264_SEI_FPA_TYPE_2D = 6,
} H264_SEI_FpaType;
+typedef struct H264SEITimeCode {
+ /* When not continuously receiving full timecodes, we have to reference
+ the previous timecode received */
+ int full;
+ int frame;
+ int seconds;
+ int minutes;
+ int hours;
+ int dropframe;
+} H264SEITimeCode;
+
typedef struct H264SEIPictureTiming {
int present;
H264_SEI_PicStructType pic_struct;
@@ -88,14 +99,15 @@ typedef struct H264SEIPictureTiming {
*/
int cpb_removal_delay;
- /* When not continuously receiving full timecodes, we have to reference
- the previous timecode received */
- int fulltc_received;
- int tc_frames;
- int tc_seconds;
- int tc_minutes;
- int tc_hours;
- int tc_dropframe;
+ /**
+ * Maximum three timecodes in a pic_timing SEI.
+ */
+ H264SEITimeCode timecode[3];
+
+ /**
+ * Number of timecode in use
+ */
+ int timecode_cnt;
} H264SEIPictureTiming;
typedef struct H264SEIAFD {
diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c
index 973f5761ef..aaf0006a32 100644
--- a/libavcodec/h264_slice.c
+++ b/libavcodec/h264_slice.c
@@ -1287,42 +1287,49 @@ static int h264_export_frame_props(H264Context *h)
h->avctx->properties |= FF_CODEC_PROPERTY_CLOSED_CAPTIONS;
}
- if (h->sei.picture_timing.fulltc_received) {
+ if (h->sei.picture_timing.timecode_cnt > 0) {
uint32_t tc = 0;
- uint32_t frames;
+ uint32_t *tc_sd;
AVFrameSideData *tcside = av_frame_new_side_data(cur->f,
AV_FRAME_DATA_S12M_TIMECODE,
- sizeof(uint32_t));
+ sizeof(uint32_t)*4);
if (!tcside)
return AVERROR(ENOMEM);
- /* For SMPTE 12-M timecodes, frame count is a special case if > 30 FPS.
- See SMPTE ST 12-1:2014 Sec 12.1 for more info. */
- if (av_cmp_q(h->avctx->framerate, (AVRational) {30, 1}) == 1) {
- frames = h->sei.picture_timing.tc_frames / 2;
- if (h->sei.picture_timing.tc_frames % 2 == 1) {
- if (av_cmp_q(h->avctx->framerate, (AVRational) {50, 1}) == 0)
- tc |= (1 << 7);
- else
- tc |= (1 << 23);
+ tc_sd = (uint32_t*)tcside->data;
+ tc_sd[0] = h->sei.picture_timing.timecode_cnt;
+
+ for (int i = 0; i < tc_sd[0]; i++) {
+ uint32_t frames;
+
+ /* For SMPTE 12-M timecodes, frame count is a special case if > 30 FPS.
+ See SMPTE ST 12-1:2014 Sec 12.1 for more info. */
+ if (av_cmp_q(h->avctx->framerate, (AVRational) {30, 1}) == 1) {
+ frames = h->sei.picture_timing.timecode[i].frame / 2;
+ if (h->sei.picture_timing.timecode[i].frame % 2 == 1) {
+ if (av_cmp_q(h->avctx->framerate, (AVRational) {50, 1}) == 0)
+ tc |= (1 << 7);
+ else
+ tc |= (1 << 23);
+ }
+ } else {
+ frames = h->sei.picture_timing.timecode[i].frame;
}
- } else {
- frames = h->sei.picture_timing.tc_frames;
- }
- tc |= h->sei.picture_timing.tc_dropframe << 30;
- tc |= (frames / 10) << 28;
- tc |= (frames % 10) << 24;
- tc |= (h->sei.picture_timing.tc_seconds / 10) << 20;
- tc |= (h->sei.picture_timing.tc_seconds % 10) << 16;
- tc |= (h->sei.picture_timing.tc_minutes / 10) << 12;
- tc |= (h->sei.picture_timing.tc_minutes % 10) << 8;
- tc |= (h->sei.picture_timing.tc_hours / 10) << 4;
- tc |= (h->sei.picture_timing.tc_hours % 10);
+ tc |= h->sei.picture_timing.timecode[i].dropframe << 30;
+ tc |= (frames / 10) << 28;
+ tc |= (frames % 10) << 24;
+ tc |= (h->sei.picture_timing.timecode[i].seconds / 10) << 20;
+ tc |= (h->sei.picture_timing.timecode[i].seconds % 10) << 16;
+ tc |= (h->sei.picture_timing.timecode[i].minutes / 10) << 12;
+ tc |= (h->sei.picture_timing.timecode[i].minutes % 10) << 8;
+ tc |= (h->sei.picture_timing.timecode[i].hours / 10) << 4;
+ tc |= (h->sei.picture_timing.timecode[i].hours % 10);
- memcpy(tcside->data, &tc, sizeof(uint32_t));
- h->sei.picture_timing.fulltc_received = 0;
+ tc_sd[i + 1] = tc;
+ }
+ h->sei.picture_timing.timecode_cnt = 0;
}
if (h->sei.alternative_transfer.present &&
diff --git a/libavfilter/vf_showinfo.c b/libavfilter/vf_showinfo.c
index d1d1415c0b..689b5399db 100644
--- a/libavfilter/vf_showinfo.c
+++ b/libavfilter/vf_showinfo.c
@@ -32,6 +32,7 @@
#include "libavutil/spherical.h"
#include "libavutil/stereo3d.h"
#include "libavutil/timestamp.h"
+#include "libavutil/timecode.h"
#include "avfilter.h"
#include "internal.h"
@@ -174,6 +175,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
case AV_FRAME_DATA_STEREO3D:
dump_stereo3d(ctx, sd);
break;
+ case AV_FRAME_DATA_S12M_TIMECODE: {
+ uint32_t *tc = (uint32_t*)sd->data;
+ for (int j = 1; j < tc[0]; j++) {
+ char tcbuf[AV_TIMECODE_STR_SIZE];
+ av_timecode_make_smpte_tc_string(tcbuf, tc[j], 0);
+ av_log(ctx, AV_LOG_INFO, "timecode - %s%s", tcbuf, j != tc[0] - 1 ? ", " : "");
+ }
+ break;
+ }
case AV_FRAME_DATA_DISPLAYMATRIX:
av_log(ctx, AV_LOG_INFO, "displaymatrix: rotation of %.2f degrees",
av_display_rotation_get((int32_t *)sd->data));
--
2.17.1
More information about the ffmpeg-devel
mailing list