1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * LRC lyrics file format decoder 3cabdff1aSopenharmony_ci * Copyright (c) 2014 StarBrilliant <m13253@hotmail.com> 4cabdff1aSopenharmony_ci * 5cabdff1aSopenharmony_ci * This file is part of FFmpeg. 6cabdff1aSopenharmony_ci * 7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 11cabdff1aSopenharmony_ci * 12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15cabdff1aSopenharmony_ci * Lesser General Public License for more details. 16cabdff1aSopenharmony_ci * 17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20cabdff1aSopenharmony_ci */ 21cabdff1aSopenharmony_ci 22cabdff1aSopenharmony_ci#include <inttypes.h> 23cabdff1aSopenharmony_ci#include <stdint.h> 24cabdff1aSopenharmony_ci#include <string.h> 25cabdff1aSopenharmony_ci 26cabdff1aSopenharmony_ci#include "avformat.h" 27cabdff1aSopenharmony_ci#include "internal.h" 28cabdff1aSopenharmony_ci#include "lrc.h" 29cabdff1aSopenharmony_ci#include "metadata.h" 30cabdff1aSopenharmony_ci#include "mux.h" 31cabdff1aSopenharmony_ci#include "version.h" 32cabdff1aSopenharmony_ci#include "libavutil/dict.h" 33cabdff1aSopenharmony_ci#include "libavutil/log.h" 34cabdff1aSopenharmony_ci#include "libavutil/macros.h" 35cabdff1aSopenharmony_ci 36cabdff1aSopenharmony_cistatic int lrc_write_header(AVFormatContext *s) 37cabdff1aSopenharmony_ci{ 38cabdff1aSopenharmony_ci const AVDictionaryEntry *metadata_item; 39cabdff1aSopenharmony_ci 40cabdff1aSopenharmony_ci if(s->nb_streams != 1 || 41cabdff1aSopenharmony_ci s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) { 42cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, 43cabdff1aSopenharmony_ci "LRC supports only a single subtitle stream.\n"); 44cabdff1aSopenharmony_ci return AVERROR(EINVAL); 45cabdff1aSopenharmony_ci } 46cabdff1aSopenharmony_ci if(s->streams[0]->codecpar->codec_id != AV_CODEC_ID_SUBRIP && 47cabdff1aSopenharmony_ci s->streams[0]->codecpar->codec_id != AV_CODEC_ID_TEXT) { 48cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Unsupported subtitle codec: %s\n", 49cabdff1aSopenharmony_ci avcodec_get_name(s->streams[0]->codecpar->codec_id)); 50cabdff1aSopenharmony_ci return AVERROR(EINVAL); 51cabdff1aSopenharmony_ci } 52cabdff1aSopenharmony_ci avpriv_set_pts_info(s->streams[0], 64, 1, 100); 53cabdff1aSopenharmony_ci 54cabdff1aSopenharmony_ci ff_standardize_creation_time(s); 55cabdff1aSopenharmony_ci ff_metadata_conv_ctx(s, ff_lrc_metadata_conv, NULL); 56cabdff1aSopenharmony_ci if(!(s->flags & AVFMT_FLAG_BITEXACT)) { // avoid breaking regression tests 57cabdff1aSopenharmony_ci /* LRC provides a metadata slot for specifying encoder version 58cabdff1aSopenharmony_ci * in addition to encoder name. We will store LIBAVFORMAT_VERSION 59cabdff1aSopenharmony_ci * to it. 60cabdff1aSopenharmony_ci */ 61cabdff1aSopenharmony_ci av_dict_set(&s->metadata, "ve", AV_STRINGIFY(LIBAVFORMAT_VERSION), 0); 62cabdff1aSopenharmony_ci } else { 63cabdff1aSopenharmony_ci av_dict_set(&s->metadata, "ve", NULL, 0); 64cabdff1aSopenharmony_ci } 65cabdff1aSopenharmony_ci for(metadata_item = NULL; 66cabdff1aSopenharmony_ci (metadata_item = av_dict_get(s->metadata, "", metadata_item, 67cabdff1aSopenharmony_ci AV_DICT_IGNORE_SUFFIX));) { 68cabdff1aSopenharmony_ci char *delim; 69cabdff1aSopenharmony_ci if(!metadata_item->value[0]) { 70cabdff1aSopenharmony_ci continue; 71cabdff1aSopenharmony_ci } 72cabdff1aSopenharmony_ci while((delim = strchr(metadata_item->value, '\n'))) { 73cabdff1aSopenharmony_ci *delim = ' '; 74cabdff1aSopenharmony_ci } 75cabdff1aSopenharmony_ci while((delim = strchr(metadata_item->value, '\r'))) { 76cabdff1aSopenharmony_ci *delim = ' '; 77cabdff1aSopenharmony_ci } 78cabdff1aSopenharmony_ci avio_printf(s->pb, "[%s:%s]\n", 79cabdff1aSopenharmony_ci metadata_item->key, metadata_item->value); 80cabdff1aSopenharmony_ci } 81cabdff1aSopenharmony_ci avio_w8(s->pb, '\n'); 82cabdff1aSopenharmony_ci return 0; 83cabdff1aSopenharmony_ci} 84cabdff1aSopenharmony_ci 85cabdff1aSopenharmony_cistatic int lrc_write_packet(AVFormatContext *s, AVPacket *pkt) 86cabdff1aSopenharmony_ci{ 87cabdff1aSopenharmony_ci if(pkt->pts != AV_NOPTS_VALUE) { 88cabdff1aSopenharmony_ci const uint8_t *line = pkt->data; 89cabdff1aSopenharmony_ci const uint8_t *end = pkt->data + pkt->size; 90cabdff1aSopenharmony_ci 91cabdff1aSopenharmony_ci while (end > line && (end[-1] == '\n' || end[-1] == '\r')) 92cabdff1aSopenharmony_ci end--; 93cabdff1aSopenharmony_ci if (line != end) { 94cabdff1aSopenharmony_ci while (line[0] == '\n' || line[0] == '\r') 95cabdff1aSopenharmony_ci line++; // Skip first empty lines 96cabdff1aSopenharmony_ci } 97cabdff1aSopenharmony_ci 98cabdff1aSopenharmony_ci while(line) { 99cabdff1aSopenharmony_ci const uint8_t *next_line = memchr(line, '\n', end - line); 100cabdff1aSopenharmony_ci size_t size = end - line; 101cabdff1aSopenharmony_ci 102cabdff1aSopenharmony_ci if (next_line) { 103cabdff1aSopenharmony_ci size = next_line - line; 104cabdff1aSopenharmony_ci if (next_line > line && next_line[-1] == '\r') 105cabdff1aSopenharmony_ci size--; 106cabdff1aSopenharmony_ci next_line++; 107cabdff1aSopenharmony_ci } 108cabdff1aSopenharmony_ci if(line[0] == '[') { 109cabdff1aSopenharmony_ci av_log(s, AV_LOG_WARNING, 110cabdff1aSopenharmony_ci "Subtitle starts with '[', may cause problems with LRC format.\n"); 111cabdff1aSopenharmony_ci } 112cabdff1aSopenharmony_ci 113cabdff1aSopenharmony_ci /* Offset feature of LRC can easily make pts negative, 114cabdff1aSopenharmony_ci * we just output it directly and let the player drop it. */ 115cabdff1aSopenharmony_ci avio_write(s->pb, "[-", 1 + (pkt->pts < 0)); 116cabdff1aSopenharmony_ci avio_printf(s->pb, "%02"PRIu64":%02"PRIu64".%02"PRIu64"]", 117cabdff1aSopenharmony_ci (FFABS64U(pkt->pts) / 6000), 118cabdff1aSopenharmony_ci ((FFABS64U(pkt->pts) / 100) % 60), 119cabdff1aSopenharmony_ci (FFABS64U(pkt->pts) % 100)); 120cabdff1aSopenharmony_ci 121cabdff1aSopenharmony_ci avio_write(s->pb, line, size); 122cabdff1aSopenharmony_ci avio_w8(s->pb, '\n'); 123cabdff1aSopenharmony_ci line = next_line; 124cabdff1aSopenharmony_ci } 125cabdff1aSopenharmony_ci } 126cabdff1aSopenharmony_ci return 0; 127cabdff1aSopenharmony_ci} 128cabdff1aSopenharmony_ci 129cabdff1aSopenharmony_ciconst AVOutputFormat ff_lrc_muxer = { 130cabdff1aSopenharmony_ci .name = "lrc", 131cabdff1aSopenharmony_ci .long_name = NULL_IF_CONFIG_SMALL("LRC lyrics"), 132cabdff1aSopenharmony_ci .extensions = "lrc", 133cabdff1aSopenharmony_ci .priv_data_size = 0, 134cabdff1aSopenharmony_ci .write_header = lrc_write_header, 135cabdff1aSopenharmony_ci .write_packet = lrc_write_packet, 136cabdff1aSopenharmony_ci .flags = AVFMT_VARIABLE_FPS | AVFMT_GLOBALHEADER | 137cabdff1aSopenharmony_ci AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT, 138cabdff1aSopenharmony_ci .subtitle_codec = AV_CODEC_ID_SUBRIP 139cabdff1aSopenharmony_ci}; 140