FFmpeg
Loading...
Searching...
No Matches
lrcdec.c
Go to the documentation of this file.
1/*
2 * LRC lyrics file format demuxer
3 * Copyright (c) 2014 StarBrilliant <m13253@hotmail.com>
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include <inttypes.h>
23#include <stdint.h>
24#include <string.h>
25
26#include "avformat.h"
27#include "demux.h"
28#include "internal.h"
29#include "lrc.h"
30#include "metadata.h"
31#include "subtitles.h"
32#include "libavutil/bprint.h"
33#include "libavutil/dict.h"
34
35typedef struct LRCContext {
37 int64_t ts_offset; // offset metadata item
39
40static int64_t find_header(const char *p)
41{
42 int64_t offset = 0;
43 while(p[offset] == ' ' || p[offset] == '\t') {
44 offset++;
45 }
46 if(p[offset] == '[' && p[offset + 1] >= 'a' && p[offset + 1] <= 'z') {
47 return offset;
48 } else {
49 return -1;
50 }
51}
52
53static int64_t count_ts(const char *p)
54{
55 int64_t offset = 0;
56 int in_brackets = 0;
57
58 for(;;) {
59 if(p[offset] == ' ' || p[offset] == '\t') {
60 offset++;
61 } else if(p[offset] == '[') {
62 offset++;
63 in_brackets++;
64 } else if (p[offset] == ']' && in_brackets) {
65 offset++;
66 in_brackets--;
67 } else if(in_brackets &&
68 (p[offset] == ':' || p[offset] == '.' || p[offset] == '-' ||
69 (p[offset] >= '0' && p[offset] <= '9'))) {
70 offset++;
71 } else {
72 break;
73 }
74 }
75 return offset;
76}
77
78static int64_t read_ts(const char *p, int64_t *start)
79{
80 int64_t offset = 0;
81 uint32_t mm;
82 double ss;
83 char prefix[3];
84
85 while(p[offset] == ' ' || p[offset] == '\t') {
86 offset++;
87 }
88 if(p[offset] != '[') {
89 return 0;
90 }
91 int ret = av_sscanf(p, "%2[[-]%"SCNu32":%lf]", prefix, &mm, &ss);
92 if (ret != 3 || prefix[0] != '[' || ss < 0 || ss > 60 || !isfinite(ss)) {
93 return 0;
94 }
95 *start = llrint((mm * 60 + ss) * AV_TIME_BASE);
96 if (prefix[1] == '-') {
97 *start = - *start;
98 }
99 do {
100 offset++;
101 } while(p[offset] && p[offset-1] != ']');
102 return offset;
103}
104
105static int64_t read_line(AVBPrint *buf, AVIOContext *pb)
106{
107 int64_t pos = avio_tell(pb);
108
109 av_bprint_clear(buf);
110 while(!avio_feof(pb)) {
111 int c = avio_r8(pb);
112 if(c != '\r') {
113 av_bprint_chars(buf, c, 1);
114 }
115 if(c == '\n') {
116 break;
117 }
118 }
119 return pos;
120}
121
122static int lrc_probe(const AVProbeData *p)
123{
124 int64_t offset = 0;
125 int64_t mm;
126 uint64_t ss, cs;
127 const AVMetadataConv *metadata_item;
128
129 if(!memcmp(p->buf, "\xef\xbb\xbf", 3)) { // Skip UTF-8 BOM header
130 offset += 3;
131 }
132 while(p->buf[offset] == '\n' || p->buf[offset] == '\r') {
133 offset++;
134 }
135 if(p->buf[offset] != '[') {
136 return 0;
137 }
138 offset++;
139 // Common metadata item but not exist in ff_lrc_metadata_conv
140 if(!memcmp(p->buf + offset, "offset:", 7)) {
141 return 40;
142 }
143 if(sscanf(p->buf + offset, "%"SCNd64":%"SCNu64".%"SCNu64"]",
144 &mm, &ss, &cs) == 3) {
145 return 50;
146 }
147 // Metadata items exist in ff_lrc_metadata_conv
148 for(metadata_item = ff_lrc_metadata_conv;
149 metadata_item->native; metadata_item++) {
150 size_t metadata_item_len = strlen(metadata_item->native);
151 if(p->buf[offset + metadata_item_len] == ':' &&
152 !memcmp(p->buf + offset, metadata_item->native, metadata_item_len)) {
153 return 40;
154 }
155 }
156 return 5; // Give it 5 scores since it starts with a bracket
157}
158
160{
161 LRCContext *lrc = s->priv_data;
162 AVBPrint line;
163 AVStream *st;
164
166 if(!st) {
167 return AVERROR(ENOMEM);
168 }
170 lrc->ts_offset = 0;
174
175 while(!avio_feof(s->pb)) {
176 int64_t header_offset, pos = read_line(&line, s->pb);
177
179 goto err_nomem_out;
180 header_offset = find_header(line.str);
181 if(header_offset >= 0) {
182 char *comma_offset = strchr(line.str, ':');
183 if(comma_offset) {
184 char *right_bracket_offset = strchr(line.str, ']');
185 if(!right_bracket_offset) {
186 continue;
187 }
188
189 *right_bracket_offset = *comma_offset = '\0';
190 if(strcmp(line.str + 1, "offset") ||
191 sscanf(comma_offset + 1, "%"SCNd64, &lrc->ts_offset) != 1) {
192 av_dict_set(&s->metadata, line.str + 1, comma_offset + 1, 0);
193 }
194 lrc->ts_offset = av_clip64(lrc->ts_offset, INT64_MIN/4, INT64_MAX/4);
195
196 *comma_offset = ':';
197 *right_bracket_offset = ']';
198 }
199
200 } else {
201 AVPacket *sub;
202 int64_t ts_start = AV_NOPTS_VALUE;
203 int64_t ts_stroffset = 0;
204 int64_t ts_stroffset_incr = 0;
205 int64_t ts_strlength = count_ts(line.str);
206
207 while((ts_stroffset_incr = read_ts(line.str + ts_stroffset,
208 &ts_start)) != 0) {
209 ts_start = av_clip64(ts_start, INT64_MIN/4, INT64_MAX/4);
210 ts_stroffset += ts_stroffset_incr;
211 sub = ff_subtitles_queue_insert(&lrc->q, line.str + ts_strlength,
212 line.len - ts_strlength, 0);
213 if (!sub)
214 goto err_nomem_out;
215 sub->pos = pos;
216 sub->pts = ts_start - lrc->ts_offset;
217 sub->duration = -1;
218 }
219 }
220 }
224 return 0;
225err_nomem_out:
227 return AVERROR(ENOMEM);
228}
229
231 .p.name = "lrc",
232 .p.long_name = NULL_IF_CONFIG_SMALL("LRC lyrics"),
233 .priv_data_size = sizeof (LRCContext),
234 .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP,
239 .read_seek2 = ff_subtitles_read_seek
240};
const FFInputFormat ff_lrc_demuxer
Definition lrcdec.c:230
void avpriv_set_pts_info(AVStream *st, int pts_wrap_bits, unsigned int pts_num, unsigned int pts_den)
Set the time base and wrapping info for a given stream.
Definition avformat.c:834
Main libavformat public API header.
int avio_feof(AVIOContext *s)
Similar to feof() but also returns nonzero on read errors.
Definition aviobuf.c:349
static av_always_inline int64_t avio_tell(AVIOContext *s)
ftell() equivalent for AVIOContext.
Definition avio.h:494
int avio_r8(AVIOContext *s)
Definition aviobuf.c:606
static int read_packet(void *opaque, uint8_t *buf, int buf_size)
int av_sscanf(const char *string, const char *format,...)
Definition avsscanf.c:962
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition bprint.c:69
AVBPrint public header.
#define AV_BPRINT_SIZE_UNLIMITED
#define ss(width, name, subs,...)
Definition cbs_vp9.c:202
#define s(width, name)
Definition cbs_vp9.c:198
static int read_probe(const AVProbeData *p)
Definition cdg.c:30
#define av_clip64
Definition common.h:103
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
#define FF_INFMT_FLAG_INIT_CLEANUP
For an FFInputFormat with this flag set read_close() needs to be called by the caller upon read_heade...
Definition demux.h:35
Public dictionary API.
static int read_header(FFV1Context *f, RangeCoder *c)
Definition ffv1dec.c:578
@ AV_CODEC_ID_TEXT
raw UTF-8 text
Definition codec_id.h:568
AVStream * avformat_new_stream(AVFormatContext *s, const struct AVCodec *c)
Add a new stream to a media file.
static int av_bprint_is_complete(const AVBPrint *buf)
Test if the print buffer is complete (not truncated).
Definition bprint.h:218
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition bprint.c:235
void av_bprint_chars(AVBPrint *buf, char c, unsigned n)
Append char c n times to a print buffer.
Definition bprint.c:130
void av_bprint_clear(AVBPrint *buf)
Reset the string to "" but keep internal allocated data.
Definition bprint.c:227
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags)
Set the given entry in *pm, overwriting an existing entry.
Definition dict.c:86
#define AVERROR(e)
Definition error.h:45
@ AVMEDIA_TYPE_SUBTITLE
Definition avutil.h:203
#define AV_NOPTS_VALUE
Undefined timestamp value.
Definition avutil.h:247
#define AV_TIME_BASE
Internal time base represented as integer.
Definition avutil.h:253
unsigned offset
Definition libaomenc.c:763
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
static av_cold int read_close(AVFormatContext *ctx)
Definition libcdio.c:143
#define llrint(x)
Definition libm.h:396
#define isfinite(x)
Definition libm.h:361
const AVMetadataConv ff_lrc_metadata_conv[]
Definition lrc.c:25
static int lrc_read_header(AVFormatContext *s)
Definition lrcdec.c:159
static int64_t read_line(AVBPrint *buf, AVIOContext *pb)
Definition lrcdec.c:105
static int lrc_probe(const AVProbeData *p)
Definition lrcdec.c:122
static int64_t find_header(const char *p)
Definition lrcdec.c:40
static int64_t count_ts(const char *p)
Definition lrcdec.c:53
static int64_t read_ts(const char *p, int64_t *start)
Definition lrcdec.c:78
void ff_metadata_conv_ctx(AVFormatContext *ctx, const AVMetadataConv *d_conv, const AVMetadataConv *s_conv)
Definition metadata.c:59
internal metadata API header see avformat.h or the public API!
unsigned int pos
Definition spdifenc.c:431
enum AVMediaType codec_type
General type of the encoded data.
Definition codec_par.h:53
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition codec_par.h:57
Format I/O context.
Definition avformat.h:1333
Bytestream IO Context.
Definition avio.h:160
const char * native
Definition metadata.h:35
This structure stores compressed data.
Definition packet.h:580
int64_t duration
Duration of this packet in AVStream->time_base units, 0 if unknown.
Definition packet.h:621
int64_t pts
Presentation timestamp in AVStream->time_base units; the time at which the decompressed packet will b...
Definition packet.h:596
int64_t pos
byte position in stream, -1 if unknown
Definition packet.h:623
This structure contains the data a format has to probe a file.
Definition avformat.h:471
Stream structure.
Definition avformat.h:766
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition avformat.h:789
FFDemuxSubtitlesQueue q
Definition lrcdec.c:36
int64_t ts_offset
Definition lrcdec.c:37
int ff_subtitles_read_packet(AVFormatContext *s, AVPacket *pkt)
Definition subtitles.c:331
AVPacket * ff_subtitles_queue_insert(FFDemuxSubtitlesQueue *q, const uint8_t *event, size_t len, int merge)
Insert a new subtitle event.
Definition subtitles.c:111
int ff_subtitles_read_seek(AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
Definition subtitles.c:337
int ff_subtitles_read_close(AVFormatContext *s)
Definition subtitles.c:345
void ff_subtitles_queue_finalize(void *log_ctx, FFDemuxSubtitlesQueue *q)
Set missing durations, sort subtitles by PTS (and then byte position), and drop duplicated events.
Definition subtitles.c:212
char prefix[8]
static double c[64]