summaryrefslogtreecommitdiff
path: root/libavformat/lrcenc.c
diff options
context:
space:
mode:
authorStar Brilliant <m13253@hotmail.com>2014-07-09 14:24:05 +0800
committerMichael Niedermayer <michaelni@gmx.at>2014-07-13 19:18:31 +0200
commit3f815f713bef1cc72e8f15df0f706971f5cd18b5 (patch)
tree2292fb351cc31cd2d0eab60c82cb63379c8e7161 /libavformat/lrcenc.c
parent42c1cc35b7623ce76c7b55c6bc100f135e17cd4f (diff)
downloadffmpeg-3f815f713bef1cc72e8f15df0f706971f5cd18b5.tar.gz
AVFormat: LRC demuxer and muxer
Reviewed-by: Derek Buitenhuis <derek.buitenhuis@gmail.com> Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
Diffstat (limited to 'libavformat/lrcenc.c')
-rw-r--r--libavformat/lrcenc.c152
1 files changed, 152 insertions, 0 deletions
diff --git a/libavformat/lrcenc.c b/libavformat/lrcenc.c
new file mode 100644
index 0000000000..c1afc2ccac
--- /dev/null
+++ b/libavformat/lrcenc.c
@@ -0,0 +1,152 @@
+/*
+ * LRC lyrics file format decoder
+ * Copyright (c) 2014 StarBrilliant <m13253@hotmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "avformat.h"
+#include "internal.h"
+#include "lrc.h"
+#include "metadata.h"
+#include "subtitles.h"
+#include "version.h"
+#include "libavutil/bprint.h"
+#include "libavutil/dict.h"
+#include "libavutil/log.h"
+#include "libavutil/macros.h"
+
+static int lrc_write_header(AVFormatContext *s)
+{
+ const AVDictionaryEntry *metadata_item;
+
+ if(s->nb_streams != 1 ||
+ s->streams[0]->codec->codec_type != AVMEDIA_TYPE_SUBTITLE) {
+ av_log(s, AV_LOG_ERROR,
+ "LRC supports only a single subtitle stream.\n");
+ return AVERROR(EINVAL);
+ }
+ if(s->streams[0]->codec->codec_id != AV_CODEC_ID_SUBRIP &&
+ s->streams[0]->codec->codec_id != AV_CODEC_ID_TEXT) {
+ av_log(s, AV_LOG_ERROR, "Unsupported subtitle codec: %s\n",
+ avcodec_get_name(s->streams[0]->codec->codec_id));
+ return AVERROR(EINVAL);
+ }
+ avpriv_set_pts_info(s->streams[0], 64, 1, 100);
+
+ ff_metadata_conv_ctx(s, ff_lrc_metadata_conv, NULL);
+ if(!(s->flags & AVFMT_FLAG_BITEXACT)) { // avoid breaking regression tests
+ /* LRC provides a metadata slot for specifying encoder version
+ * in addition to encoder name. We will store LIBAVFORMAT_VERSION
+ * to it.
+ */
+ av_dict_set(&s->metadata, "ve", AV_STRINGIFY(LIBAVFORMAT_VERSION), 0);
+ } else {
+ av_dict_set(&s->metadata, "ve", NULL, 0);
+ }
+ for(metadata_item = NULL;
+ (metadata_item = av_dict_get(s->metadata, "", metadata_item,
+ AV_DICT_IGNORE_SUFFIX)) != NULL;) {
+ char *delim;
+ if(!metadata_item->value[0]) {
+ continue;
+ }
+ while((delim = strchr(metadata_item->value, '\n')) != NULL) {
+ *delim = ' ';
+ }
+ while((delim = strchr(metadata_item->value, '\r')) != NULL) {
+ *delim = ' ';
+ }
+ avio_printf(s->pb, "[%s:%s]\n",
+ metadata_item->key, metadata_item->value);
+ }
+ avio_printf(s->pb, "\n");
+ return 0;
+}
+
+static int lrc_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ if(pkt->pts != AV_NOPTS_VALUE) {
+ char *data = av_malloc(pkt->size + 1);
+ char *line;
+ char *delim;
+
+ if(!data) {
+ return AVERROR(ENOMEM);
+ }
+ memcpy(data, pkt->data, pkt->size);
+ data[pkt->size] = '\0';
+
+ for(delim = data + pkt->size - 1;
+ delim >= data && (delim[0] == '\n' || delim[0] == '\r'); delim--) {
+ delim[0] = '\0'; // Strip last empty lines
+ }
+ line = data;
+ while(line[0] == '\n' || line[0] == '\r') {
+ line++; // Skip first empty lines
+ }
+
+ while(line) {
+ delim = strchr(line, '\n');
+ if(delim) {
+ if(delim > line && delim[-1] == '\r') {
+ delim[-1] = '\0';
+ }
+ delim[0] = '\0';
+ delim++;
+ }
+ if(line[0] == '[') {
+ av_log(s, AV_LOG_WARNING,
+ "Subtitle starts with '[', may cause problems with LRC format.\n");
+ }
+
+ if(pkt->pts >= 0) {
+ avio_printf(s->pb, "[%02"PRId64":%02"PRId64".%02"PRId64"]",
+ (pkt->pts / 6000),
+ ((pkt->pts / 100) % 60),
+ (pkt->pts % 100));
+ } else {
+ /* Offset feature of LRC can easily make pts negative,
+ * we just output it directly and let the player drop it. */
+ avio_printf(s->pb, "[-%02"PRId64":%02"PRId64".%02"PRId64"]",
+ (-pkt->pts) / 6000,
+ ((-pkt->pts) / 100) % 60,
+ (-pkt->pts) % 100);
+ }
+ avio_printf(s->pb, "%s\n", line);
+ line = delim;
+ }
+ av_free(data);
+ }
+ return 0;
+}
+
+AVOutputFormat ff_lrc_muxer = {
+ .name = "lrc",
+ .long_name = NULL_IF_CONFIG_SMALL("LRC lyrics"),
+ .extensions = "lrc",
+ .priv_data_size = 0,
+ .write_header = lrc_write_header,
+ .write_packet = lrc_write_packet,
+ .flags = AVFMT_VARIABLE_FPS | AVFMT_GLOBALHEADER |
+ AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT,
+ .subtitle_codec = AV_CODEC_ID_SUBRIP
+};