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