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 
70 MusicMetadata* MetaIOMP4::read(const QString &filename)
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  MusicMetadata *retdata = new MusicMetadata(filename,
128  artist,
129  compilation ? artist : "",
130  album,
131  title,
132  genre,
133  year,
134  tracknum,
135  length);
136 
137  retdata->setCompilation(compilation);
138 
139  avformat_close_input(&p_context);
140 
141  return retdata;
142 }
143 
151 QString MetaIOMP4::getFieldValue(AVFormatContext* context, const char* tagname)
152 {
153  AVDictionaryEntry *tag = av_dict_get(context->metadata, tagname, nullptr, 0);
154 
155  QString value;
156 
157  if (tag)
158  value = QString::fromUtf8(tag->value);
159 
160  return value;
161 }
162 
169 int MetaIOMP4::getTrackLength(const QString &filename)
170 {
171  AVFormatContext* p_context = nullptr;
172  AVInputFormat* p_inputformat = nullptr;
173 
174  // Open the specified file and populate the metadata info
175  QByteArray local8bit = filename.toLocal8Bit();
176  if ((avformat_open_input(&p_context, local8bit.constData(),
177  p_inputformat, nullptr) < 0))
178  {
179  return 0;
180  }
181 
182  if (avformat_find_stream_info(p_context, nullptr) < 0)
183  return 0;
184 
185  int rv = getTrackLength(p_context);
186 
187  avformat_close_input(&p_context);
188 
189  return rv;
190 }
191 
198 int MetaIOMP4::getTrackLength(AVFormatContext* pContext)
199 {
200  if (!pContext)
201  return 0;
202 
203  av_estimate_timings(pContext, 0);
204 
205  return (pContext->duration / AV_TIME_BASE) * 1000;
206 }
207 
216 void MetaIOMP4::metadataSanityCheck(QString *artist, QString *album,
217  QString *title, QString *genre)
218 {
219  if (artist->isEmpty())
220  artist->append("Unknown Artist");
221 
222  if (album->isEmpty())
223  album->append("Unknown Album");
224 
225  if (title->isEmpty())
226  title->append("Unknown Title");
227 
228  if (genre->isEmpty())
229  genre->append("Unknown Genre");
230 }
void metadataSanityCheck(QString *artist, QString *album, QString *title, QString *genre)
Replace any empty strings in extracted metadata with sane defaults.
Definition: metaiomp4.cpp:216
QString getFieldValue(AVFormatContext *context, const char *tagname)
Retrieve the value of a named metadata field.
Definition: metaiomp4.cpp:151
void setCompilation(bool state)
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:103
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:169