MythTV  master
metaiomp4.cpp
Go to the documentation of this file.
1 
2 // libmythmetadata
3 #include "metaiomp4.h"
4 #include "musicmetadata.h"
5 
6 // Libmyth
7 #include <mythlogging.h>
8 #include <mythcontext.h>
9 
10 // Libav*
11 extern "C" {
12 #include <libavformat/avformat.h>
13 #include <libavcodec/avcodec.h>
14 }
15 
19 bool MetaIOMP4::write(const QString &filename, MusicMetadata* mdata)
20 {
21  if (!mdata)
22  return false;
23 
24  if (filename.isEmpty())
25  return false;
26 
27 // Disabled because it doesn't actually work. Better implemented with Taglib
28 // when we formally move to 1.6
29 
30 // AVFormatContext* p_context = nullptr;
31 // AVFormatParameters* p_params = nullptr;
32 // AVInputFormat* p_inputformat = nullptr;
33 //
34 // QByteArray local8bit = filename.toLocal8Bit();
35 // if ((av_open_input_file(&p_context, local8bit.constData(),
36 // p_inputformat, 0, p_params) < 0))
37 // {
38 // return nullptr;
39 // }
40 //
41 // if (av_find_stream_info(p_context) < 0)
42 // return nullptr;
43 //
44 // QByteArray artist = mdata->Artist().toUtf8();
45 // QByteArray album = mdata->Album().toUtf8();
46 // QByteArray title = mdata->Title().toUtf8();
47 // QByteArray genre = mdata->Genre().toUtf8();
48 // QByteArray date = QString::number(mdata->Year()).toUtf8();
49 // QByteArray track = QString::number(mdata->Track()).toUtf8();
50 // QByteArray comp = QString(mdata->Compilation() ? "1" : "0").toUtf8();
51 //
52 // AVMetadata* avmetadata = p_context->metadata;
53 //
54 // av_metadata_set(&avmetadata, "author", artist.constData());
55 // av_metadata_set(&avmetadata, "album", album.constData());
56 // av_metadata_set(&avmetadata, "title", title.constData());
57 // av_metadata_set(&avmetadata, "genre", genre.constData());
58 // av_metadata_set(&avmetadata, "year", date.constData());
59 // av_metadata_set(&avmetadata, "track", track.constData());
60 // av_metadata_set(&avmetadata, "compilation", comp.constData());
61 //
62 // av_close_input_file(p_context);
63 
64  return true;
65 }
66 
71 {
72  QString title, artist, album, genre;
73  int year = 0, tracknum = 0, length = 0;
74  bool compilation = false;
75 
76  AVFormatContext* p_context = nullptr;
77  AVInputFormat* p_inputformat = nullptr;
78 
79  QByteArray local8bit = filename.toLocal8Bit();
80  if ((avformat_open_input(&p_context, local8bit.constData(),
81  p_inputformat, nullptr) < 0))
82  {
83  return nullptr;
84  }
85 
86  if (avformat_find_stream_info(p_context, nullptr) < 0)
87  return nullptr;
88 
89 #if 0
90  //### Debugging, enable to dump a list of all field names/values found
91 
92  AVDictionaryEntry *tag = av_dict_get(p_context->metadata, "\0", nullptr,
93  AV_METADATA_IGNORE_SUFFIX);
94  while (tag != nullptr)
95  {
96  LOG(VB_GENERAL, LOG_DEBUG, QString("Tag: %1 Value: %2")
97  .arg(tag->key) .arg(QString::fromUtf8(tag->value)));
98  tag = av_dict_get(p_context->metadata, "\0", tag,
99  AV_METADATA_IGNORE_SUFFIX);
100  }
101  //####
102 #endif
103 
104  title = getFieldValue(p_context, "title");
105  if (title.isEmpty())
106  {
107  readFromFilename(filename, artist, album, title, genre, tracknum);
108  }
109  else
110  {
111  title = getFieldValue(p_context, "title");
112  artist = getFieldValue(p_context, "author");
113  // Author is the correct fieldname, but
114  // we've been saving to artist for years
115  if (artist.isEmpty())
116  artist = getFieldValue(p_context, "artist");
117  album = getFieldValue(p_context, "album");
118  year = getFieldValue(p_context, "year").toInt();
119  genre = getFieldValue(p_context, "genre");
120  tracknum = getFieldValue(p_context, "track").toInt();
121  compilation = (getFieldValue(p_context, "").toInt() != 0);
122  length = getTrackLength(p_context);
123  }
124 
125  metadataSanityCheck(&artist, &album, &title, &genre);
126 
127  auto *retdata = new MusicMetadata(filename, artist,
128  compilation ? artist : "",
129  album, title, genre, year,
130  tracknum, length);
131 
132  retdata->setCompilation(compilation);
133 
134  avformat_close_input(&p_context);
135 
136  return retdata;
137 }
138 
146 QString MetaIOMP4::getFieldValue(AVFormatContext* context, const char* tagname)
147 {
148  AVDictionaryEntry *tag = av_dict_get(context->metadata, tagname, nullptr, 0);
149 
150  QString value;
151 
152  if (tag)
153  value = QString::fromUtf8(tag->value);
154 
155  return value;
156 }
157 
165 {
166  AVFormatContext* p_context = nullptr;
167  AVInputFormat* p_inputformat = nullptr;
168 
169  // Open the specified file and populate the metadata info
170  QByteArray local8bit = filename.toLocal8Bit();
171  if ((avformat_open_input(&p_context, local8bit.constData(),
172  p_inputformat, nullptr) < 0))
173  {
174  return 0;
175  }
176 
177  if (avformat_find_stream_info(p_context, nullptr) < 0)
178  return 0;
179 
180  int rv = getTrackLength(p_context);
181 
182  avformat_close_input(&p_context);
183 
184  return rv;
185 }
186 
193 int MetaIOMP4::getTrackLength(AVFormatContext* pContext)
194 {
195  if (!pContext)
196  return 0;
197 
198  av_estimate_timings(pContext, 0);
199 
200  return (pContext->duration / AV_TIME_BASE) * 1000;
201 }
202 
211 void MetaIOMP4::metadataSanityCheck(QString *artist, QString *album,
212  QString *title, QString *genre)
213 {
214  if (artist->isEmpty())
215  artist->append("Unknown Artist");
216 
217  if (album->isEmpty())
218  album->append("Unknown Album");
219 
220  if (title->isEmpty())
221  title->append("Unknown Title");
222 
223  if (genre->isEmpty())
224  genre->append("Unknown Genre");
225 }
static void metadataSanityCheck(QString *artist, QString *album, QString *title, QString *genre)
Replace any empty strings in extracted metadata with sane defaults.
Definition: metaiomp4.cpp:211
static QString getFieldValue(AVFormatContext *context, const char *tagname)
Retrieve the value of a named metadata field.
Definition: metaiomp4.cpp:146
void readFromFilename(const QString &filename, QString &artist, QString &album, QString &title, QString &genre, int &tracknum)
Reads MusicMetadata based on the folder/filename.
Definition: metaio.cpp:102
MusicMetadata * read(const QString &filename) override
Reads MusicMetadata from a file.
Definition: metaiomp4.cpp:70
bool write(const QString &filename, MusicMetadata *mdata) override
Writes all metadata back to a file.
Definition: metaiomp4.cpp:19
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
int getTrackLength(const QString &filename) override
Find the length of the track (in seconds)
Definition: metaiomp4.cpp:164