You've already forked FFmpeg
mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-08-04 22:03:09 +02:00
avformat/lrcenc: add precision option
Allows to output LRC with more precision than standard centiseconds. Time base is determined by number of requested digits. Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
This commit is contained in:
@ -4629,4 +4629,18 @@ one byte per subtitle on average.
|
|||||||
By default, this work-around is disabled.
|
By default, this work-around is disabled.
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
|
@section lrc
|
||||||
|
|
||||||
|
This codec encodes the LRC lyrics format.
|
||||||
|
|
||||||
|
@subsection Options
|
||||||
|
|
||||||
|
@table @option
|
||||||
|
@item precision
|
||||||
|
Specify the precision of the fractional part of the timestamp. Time base is
|
||||||
|
determined based on this value.
|
||||||
|
|
||||||
|
Defaults to 2 for centiseconds.
|
||||||
|
@end table
|
||||||
|
|
||||||
@c man end SUBTITLES ENCODERS
|
@c man end SUBTITLES ENCODERS
|
||||||
|
@ -32,6 +32,12 @@
|
|||||||
#include "libavutil/dict.h"
|
#include "libavutil/dict.h"
|
||||||
#include "libavutil/log.h"
|
#include "libavutil/log.h"
|
||||||
#include "libavutil/macros.h"
|
#include "libavutil/macros.h"
|
||||||
|
#include "libavutil/opt.h"
|
||||||
|
|
||||||
|
typedef struct LRCSubtitleContext {
|
||||||
|
const AVClass *class;
|
||||||
|
int precision; ///< precision of the fractional part of the timestamp, 2 for centiseconds
|
||||||
|
} LRCSubtitleContext;
|
||||||
|
|
||||||
static int lrc_write_header(AVFormatContext *s)
|
static int lrc_write_header(AVFormatContext *s)
|
||||||
{
|
{
|
||||||
@ -43,7 +49,7 @@ static int lrc_write_header(AVFormatContext *s)
|
|||||||
avcodec_get_name(s->streams[0]->codecpar->codec_id));
|
avcodec_get_name(s->streams[0]->codecpar->codec_id));
|
||||||
return AVERROR(EINVAL);
|
return AVERROR(EINVAL);
|
||||||
}
|
}
|
||||||
avpriv_set_pts_info(s->streams[0], 64, 1, 100);
|
avpriv_set_pts_info(s->streams[0], 64, 1, AV_TIME_BASE);
|
||||||
|
|
||||||
ff_standardize_creation_time(s);
|
ff_standardize_creation_time(s);
|
||||||
ff_metadata_conv_ctx(s, ff_lrc_metadata_conv, NULL);
|
ff_metadata_conv_ctx(s, ff_lrc_metadata_conv, NULL);
|
||||||
@ -77,6 +83,8 @@ static int lrc_write_header(AVFormatContext *s)
|
|||||||
|
|
||||||
static int lrc_write_packet(AVFormatContext *s, AVPacket *pkt)
|
static int lrc_write_packet(AVFormatContext *s, AVPacket *pkt)
|
||||||
{
|
{
|
||||||
|
const LRCSubtitleContext *p = s->priv_data;
|
||||||
|
|
||||||
if(pkt->pts != AV_NOPTS_VALUE) {
|
if(pkt->pts != AV_NOPTS_VALUE) {
|
||||||
const uint8_t *line = pkt->data;
|
const uint8_t *line = pkt->data;
|
||||||
const uint8_t *end = pkt->data + pkt->size;
|
const uint8_t *end = pkt->data + pkt->size;
|
||||||
@ -88,6 +96,10 @@ static int lrc_write_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
line++; // Skip first empty lines
|
line++; // Skip first empty lines
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int frac_mult = 1;
|
||||||
|
for (int i = 0; i < p->precision; ++i)
|
||||||
|
frac_mult *= 10;
|
||||||
|
|
||||||
while(line) {
|
while(line) {
|
||||||
const uint8_t *next_line = memchr(line, '\n', end - line);
|
const uint8_t *next_line = memchr(line, '\n', end - line);
|
||||||
size_t size = end - line;
|
size_t size = end - line;
|
||||||
@ -105,11 +117,14 @@ static int lrc_write_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
|
|
||||||
/* Offset feature of LRC can easily make pts negative,
|
/* Offset feature of LRC can easily make pts negative,
|
||||||
* we just output it directly and let the player drop it. */
|
* we just output it directly and let the player drop it. */
|
||||||
|
uint64_t abs_pts = FFABS64U(pkt->pts);
|
||||||
|
uint64_t minutes = abs_pts / (60 * AV_TIME_BASE);
|
||||||
|
uint64_t seconds = (abs_pts / AV_TIME_BASE) % 60;
|
||||||
|
uint64_t fraction = abs_pts % AV_TIME_BASE;
|
||||||
|
uint64_t rescaled = av_rescale_q(fraction, AV_TIME_BASE_Q, (AVRational){1, frac_mult});
|
||||||
avio_write(s->pb, "[-", 1 + (pkt->pts < 0));
|
avio_write(s->pb, "[-", 1 + (pkt->pts < 0));
|
||||||
avio_printf(s->pb, "%02"PRIu64":%02"PRIu64".%02"PRIu64"]",
|
avio_printf(s->pb, "%02"PRIu64":%02"PRIu64".%0*"PRIu64"]",
|
||||||
(FFABS64U(pkt->pts) / 6000),
|
minutes, seconds, p->precision, rescaled);
|
||||||
((FFABS64U(pkt->pts) / 100) % 60),
|
|
||||||
(FFABS64U(pkt->pts) % 100));
|
|
||||||
|
|
||||||
avio_write(s->pb, line, size);
|
avio_write(s->pb, line, size);
|
||||||
avio_w8(s->pb, '\n');
|
avio_w8(s->pb, '\n');
|
||||||
@ -119,6 +134,20 @@ static int lrc_write_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define OFFSET(x) offsetof(LRCSubtitleContext, x)
|
||||||
|
#define SE AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_ENCODING_PARAM
|
||||||
|
static const AVOption options[] = {
|
||||||
|
{"precision", "precision of the fractional part of the timestamp, 2 for centiseconds", OFFSET(precision), AV_OPT_TYPE_INT, {.i64 = 2}, 1, 6, SE},
|
||||||
|
{ NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const AVClass lrcenc_class = {
|
||||||
|
.class_name = "lrc",
|
||||||
|
.item_name = av_default_item_name,
|
||||||
|
.option = options,
|
||||||
|
.version = LIBAVUTIL_VERSION_INT,
|
||||||
|
};
|
||||||
|
|
||||||
const FFOutputFormat ff_lrc_muxer = {
|
const FFOutputFormat ff_lrc_muxer = {
|
||||||
.p.name = "lrc",
|
.p.name = "lrc",
|
||||||
.p.long_name = NULL_IF_CONFIG_SMALL("LRC lyrics"),
|
.p.long_name = NULL_IF_CONFIG_SMALL("LRC lyrics"),
|
||||||
@ -129,7 +158,8 @@ const FFOutputFormat ff_lrc_muxer = {
|
|||||||
.p.audio_codec = AV_CODEC_ID_NONE,
|
.p.audio_codec = AV_CODEC_ID_NONE,
|
||||||
.p.subtitle_codec = AV_CODEC_ID_SUBRIP,
|
.p.subtitle_codec = AV_CODEC_ID_SUBRIP,
|
||||||
.flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
|
.flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH,
|
||||||
.priv_data_size = 0,
|
.priv_data_size = sizeof(LRCSubtitleContext),
|
||||||
.write_header = lrc_write_header,
|
.write_header = lrc_write_header,
|
||||||
.write_packet = lrc_write_packet,
|
.write_packet = lrc_write_packet,
|
||||||
|
.p.priv_class = &lrcenc_class,
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user