mirror of https://github.com/FFmpeg/FFmpeg.git
Dumps all metadata to a text file for easy manual editing. Originally committed as revision 26101 to svn://svn.ffmpeg.org/ffmpeg/trunkoldabi
parent
bb62d5c1f0
commit
a46515115c
8 changed files with 203 additions and 1 deletions
@ -0,0 +1,68 @@ |
||||
@chapter Metadata |
||||
@c man begin METADATA |
||||
|
||||
FFmpeg is able to dump metadata from media files into a simple UTF-8-encoded |
||||
INI-like text file and then load it back using the metadata muxer/demuxer. |
||||
|
||||
The file format is as follows: |
||||
@enumerate |
||||
|
||||
@item |
||||
A file consists of a header and a number of metadata tags divided into sections, |
||||
each on its own line. |
||||
|
||||
@item |
||||
The header is a ';FFMETADATA' string, followed by a version number (now 1). |
||||
|
||||
@item |
||||
Metadata tags are of the form 'key=value' |
||||
|
||||
@item |
||||
Immediately after header follows global metadata |
||||
|
||||
@item |
||||
After global metadata there may be sections with per-stream/per-chapter |
||||
metadata. |
||||
|
||||
@item |
||||
A section starts with the section name in uppercase (i.e. STREAM or CHAPTER) in |
||||
brackets ('[', ']') and ends with next section or end of file. |
||||
|
||||
@item |
||||
At the beginning of a chapter section there may be an optional timebase to be |
||||
used for start/end values. It must be in form 'TIMEBASE=num/den', where num and |
||||
den are integers. If the timebase is missing then start/end times are assumed to |
||||
be in milliseconds. |
||||
Next a chapter section must contain chapter start and end times in form |
||||
'START=num', 'END=num', where num is a positive integer. |
||||
|
||||
@item |
||||
Empty lines and lines starting with ';' or '#' are ignored. |
||||
|
||||
@item |
||||
Metadata keys or values containing special characters ('=', ';', '#', '\' and a |
||||
newline) must be escaped with a backslash '\'. |
||||
|
||||
@item |
||||
Note that whitespace in metadata (e.g. foo = bar) is considered to be a part of |
||||
the tag (in the example above key is 'foo ', value is ' bar'). |
||||
@end enumerate |
||||
|
||||
A ffmetadata file might look like this: |
||||
@example |
||||
;FFMETADATA1 |
||||
title=bike\\shed |
||||
;this is a comment |
||||
artist=FFmpeg troll team |
||||
|
||||
[CHAPTER] |
||||
TIMEBASE=1/1000 |
||||
START=0 |
||||
#chapter ends at 0:01:00 |
||||
END=60000 |
||||
title=chapter \#1 |
||||
[STREAM] |
||||
title=multi\ |
||||
line |
||||
@end example |
||||
@c man end METADATA |
@ -0,0 +1,29 @@ |
||||
/*
|
||||
* Common data for metadata muxer/demuxer |
||||
* Copyright (c) 2010 Anton Khirnov |
||||
* |
||||
* 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 |
||||
*/ |
||||
|
||||
#ifndef AVFORMAT_META_H |
||||
#define AVFORMAT_META_H |
||||
|
||||
#define ID_STRING ";FFMETADATA" |
||||
#define ID_CHAPTER "[CHAPTER]" |
||||
#define ID_STREAM "[STREAM]" |
||||
|
||||
#endif /* AVFORMAT_META_H */ |
@ -0,0 +1,100 @@ |
||||
/*
|
||||
* Metadata muxer |
||||
* Copyright (c) 2010 Anton Khirnov |
||||
* |
||||
* 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 "avformat.h" |
||||
#include "meta.h" |
||||
|
||||
|
||||
static void write_escape_str(ByteIOContext *s, const uint8_t *str) |
||||
{ |
||||
const uint8_t *p = str; |
||||
|
||||
while (*p) { |
||||
if (*p == '#' || *p == ';' || *p == '=' || *p == '\\' || *p == '\n') |
||||
put_byte(s, '\\'); |
||||
put_byte(s, *p); |
||||
p++; |
||||
} |
||||
} |
||||
|
||||
static void write_tags(ByteIOContext *s, AVMetadata *m) |
||||
{ |
||||
AVMetadataTag *t = NULL; |
||||
while ((t = av_metadata_get(m, "", t, AV_METADATA_IGNORE_SUFFIX))) { |
||||
write_escape_str(s, t->key); |
||||
put_byte(s, '='); |
||||
write_escape_str(s, t->value); |
||||
put_byte(s, '\n'); |
||||
} |
||||
} |
||||
|
||||
static int write_header(AVFormatContext *s) |
||||
{ |
||||
put_tag(s->pb, ID_STRING); |
||||
put_byte(s->pb, '1'); // version
|
||||
put_byte(s->pb, '\n'); |
||||
put_flush_packet(s->pb); |
||||
return 0; |
||||
} |
||||
|
||||
static int write_trailer(AVFormatContext *s) |
||||
{ |
||||
int i; |
||||
|
||||
write_tags(s->pb, s->metadata); |
||||
|
||||
for (i = 0; i < s->nb_streams; i++) { |
||||
put_tag(s->pb, ID_STREAM); |
||||
put_byte(s->pb, '\n'); |
||||
write_tags(s->pb, s->streams[i]->metadata); |
||||
} |
||||
|
||||
for (i = 0; i < s->nb_chapters; i++) { |
||||
AVChapter *ch = s->chapters[i]; |
||||
put_tag(s->pb, ID_CHAPTER); |
||||
put_byte(s->pb, '\n'); |
||||
url_fprintf(s->pb, "TIMEBASE=%d/%d\n", ch->time_base.num, ch->time_base.den); |
||||
url_fprintf(s->pb, "START=%lld\n", ch->start); |
||||
url_fprintf(s->pb, "END=%lld\n", ch->end); |
||||
write_tags(s->pb, ch->metadata); |
||||
} |
||||
|
||||
put_flush_packet(s->pb); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int write_packet(AVFormatContext *s, AVPacket *pkt) |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
AVOutputFormat ffmetadata_muxer = { |
||||
.name = "ffmetadata", |
||||
.long_name = NULL_IF_CONFIG_SMALL("FFmpeg metadata in text format"), |
||||
.extensions = "ffmeta", |
||||
.video_codec = CODEC_ID_NONE, |
||||
.audio_codec = CODEC_ID_NONE, |
||||
.write_header = write_header, |
||||
.write_packet = write_packet, |
||||
.write_trailer = write_trailer, |
||||
.flags = AVFMT_NOTIMESTAMPS | AVFMT_NOSTREAMS, |
||||
}; |
Loading…
Reference in new issue