You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
192 lines
6.1 KiB
192 lines
6.1 KiB
/*****************************************************************************
|
|
* pcr_helper.c:
|
|
*****************************************************************************
|
|
* Copyright (C) 2022 VLC authors and VideoLAN
|
|
*
|
|
* This program 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.
|
|
*
|
|
* This program 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 this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
|
*****************************************************************************/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <stdint.h>
|
|
|
|
#include "pcr_helper.h"
|
|
|
|
#include <vlc_list.h>
|
|
#include <vlc_tick.h>
|
|
|
|
struct transcode_track_pcr_helper
|
|
{
|
|
vlc_tick_t max_delay;
|
|
/// Represents the media time held by the frame processing unit.
|
|
vlc_tick_t held_media_time;
|
|
vlc_tick_t input_media_time;
|
|
vlc_tick_t last_dts_output;
|
|
|
|
struct vlc_list delayed_frames_data;
|
|
|
|
vlc_pcr_sync_t *sync_ref;
|
|
unsigned int pcr_sync_es_id;
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
vlc_tick_t length;
|
|
vlc_tick_t dts;
|
|
vlc_tick_t media_time;
|
|
struct vlc_list node;
|
|
} delayed_frame_data_t;
|
|
|
|
transcode_track_pcr_helper_t *transcode_track_pcr_helper_New(vlc_pcr_sync_t *sync_ref,
|
|
vlc_tick_t max_delay)
|
|
{
|
|
transcode_track_pcr_helper_t *ret = malloc(sizeof(*ret));
|
|
if (unlikely(ret == NULL))
|
|
return NULL;
|
|
|
|
if (vlc_pcr_sync_NewESID(sync_ref, &ret->pcr_sync_es_id) != VLC_SUCCESS)
|
|
{
|
|
free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
ret->max_delay = max_delay;
|
|
ret->held_media_time = 0;
|
|
ret->input_media_time = 0;
|
|
ret->last_dts_output = VLC_TICK_INVALID;
|
|
|
|
vlc_list_init(&ret->delayed_frames_data);
|
|
ret->sync_ref = sync_ref;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void transcode_track_pcr_helper_Delete(transcode_track_pcr_helper_t *pcr_helper)
|
|
{
|
|
delayed_frame_data_t *it;
|
|
vlc_list_foreach(it, &pcr_helper->delayed_frames_data, node)
|
|
{
|
|
vlc_list_remove(&it->node);
|
|
free(it);
|
|
}
|
|
|
|
vlc_pcr_sync_DelESID(pcr_helper->sync_ref, pcr_helper->pcr_sync_es_id);
|
|
|
|
free(pcr_helper);
|
|
}
|
|
|
|
static inline vlc_tick_t
|
|
transcode_track_pcr_helper_GetFramePCR(transcode_track_pcr_helper_t *pcr_helper,
|
|
vlc_tick_t frame_dts)
|
|
{
|
|
vlc_tick_t pcr = VLC_TICK_INVALID;
|
|
vlc_tick_t it = VLC_TICK_INVALID;
|
|
|
|
// XXX: `vlc_pcr_sync_SignalFrameOutput` only needs DTS for now. Passing a frame by only filling
|
|
// the DTS is enough.
|
|
const vlc_frame_t fake_frame = {.i_dts = frame_dts};
|
|
|
|
while ((it = vlc_pcr_sync_SignalFrameOutput(pcr_helper->sync_ref, pcr_helper->pcr_sync_es_id,
|
|
&fake_frame)) != VLC_TICK_INVALID)
|
|
{
|
|
pcr = it;
|
|
}
|
|
return pcr;
|
|
}
|
|
|
|
int transcode_track_pcr_helper_SignalEnteringFrame(transcode_track_pcr_helper_t *pcr_helper,
|
|
const vlc_frame_t *frame,
|
|
vlc_tick_t *dropped_frame_ts)
|
|
{
|
|
delayed_frame_data_t *bdata = malloc(sizeof(*bdata));
|
|
if (unlikely(bdata == NULL))
|
|
return VLC_ENOMEM;
|
|
|
|
pcr_helper->input_media_time += frame->i_length;
|
|
pcr_helper->held_media_time += frame->i_length;
|
|
|
|
bdata->length = frame->i_length;
|
|
bdata->dts = frame->i_dts;
|
|
bdata->media_time = pcr_helper->input_media_time;
|
|
|
|
vlc_pcr_sync_SignalFrame(pcr_helper->sync_ref, pcr_helper->pcr_sync_es_id, frame);
|
|
|
|
vlc_list_append(&bdata->node, &pcr_helper->delayed_frames_data);
|
|
|
|
// Something went wrong in the delaying unit.
|
|
// Exceeding this limit usually means the frame was dropped. So in our case, act like it went
|
|
// through.
|
|
// TODO needs to be properly unit-tested.
|
|
if (pcr_helper->held_media_time > pcr_helper->max_delay)
|
|
{
|
|
delayed_frame_data_t *first_bdata = vlc_list_first_entry_or_null(
|
|
&pcr_helper->delayed_frames_data, delayed_frame_data_t, node);
|
|
assert(first_bdata != NULL);
|
|
|
|
const vlc_tick_t pcr = transcode_track_pcr_helper_GetFramePCR(pcr_helper, first_bdata->dts);
|
|
*dropped_frame_ts = pcr_helper->last_dts_output == VLC_TICK_INVALID
|
|
? pcr
|
|
: __MIN(pcr_helper->last_dts_output, first_bdata->dts);
|
|
|
|
pcr_helper->held_media_time -= first_bdata->length;
|
|
|
|
vlc_list_remove(&first_bdata->node);
|
|
free(first_bdata);
|
|
}
|
|
else
|
|
{
|
|
*dropped_frame_ts = VLC_TICK_INVALID;
|
|
}
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
int transcode_track_pcr_helper_SignalLeavingFrame(transcode_track_pcr_helper_t *pcr_helper,
|
|
const vlc_frame_t *frame,
|
|
vlc_tick_t *pcr)
|
|
{
|
|
*pcr = VLC_TICK_INVALID;
|
|
|
|
if (vlc_list_is_empty(&pcr_helper->delayed_frames_data))
|
|
return VLC_EGENERIC;
|
|
|
|
pcr_helper->last_dts_output = frame->i_dts;
|
|
|
|
pcr_helper->held_media_time -= frame->i_length;
|
|
const vlc_tick_t output_media_time =
|
|
pcr_helper->input_media_time - pcr_helper->held_media_time;
|
|
|
|
delayed_frame_data_t *frame_data;
|
|
vlc_list_foreach(frame_data, &pcr_helper->delayed_frames_data, node)
|
|
{
|
|
if (output_media_time < frame_data->media_time)
|
|
break;
|
|
|
|
const vlc_tick_t current_pcr =
|
|
transcode_track_pcr_helper_GetFramePCR(pcr_helper, frame_data->dts);
|
|
if (current_pcr != VLC_TICK_INVALID)
|
|
*pcr = current_pcr;
|
|
|
|
vlc_list_remove(&frame_data->node);
|
|
free(frame_data);
|
|
}
|
|
|
|
if (*pcr != VLC_TICK_INVALID)
|
|
*pcr = __MIN(frame->i_dts, *pcr);
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|