00001 #include <iostream>
00002 #include <sys/stat.h>
00003 #include <fcntl.h>
00004 #include <unistd.h>
00005 #include <sys/types.h>
00006 #include <stdlib.h>
00007
00008 using namespace std;
00009
00010 #define USE_TAGGING
00011 #include "metaiomp4.h"
00012 #include "metadata.h"
00013 #include <mp4ff.h>
00014 #include <faad.h>
00015 #include <mythtv/mythcontext.h>
00016
00017
00018 MetaIOMP4::MetaIOMP4(void)
00019 : MetaIO(".mp4")
00020 {
00021 }
00022
00023 MetaIOMP4::~MetaIOMP4(void)
00024 {
00025 }
00026
00027
00028
00029
00030
00031
00032 uint32_t md_read_callback(void *user_data, void *buffer, uint32_t length)
00033 {
00034 mp4callback_data_t *file_data = (mp4callback_data_t*)user_data;
00035 return fread(buffer, 1, length, file_data->file);
00036 }
00037
00038 uint32_t md_write_callback(void *user_data, void *buffer, uint32_t length)
00039 {
00040 mp4callback_data_t *file_data = (mp4callback_data_t*)user_data;
00041 return fwrite(buffer, 1, length, file_data->file);
00042 }
00043
00044 uint32_t md_truncate_callback(void *user_data)
00045 {
00046 mp4callback_data_t *file_data = (mp4callback_data_t*)user_data;
00047 ftruncate(file_data->fd, ftello(file_data->file));
00048 return 0;
00049 }
00050
00051 uint32_t md_seek_callback(void *user_data, uint64_t position)
00052 {
00053 mp4callback_data_t *file_data = (mp4callback_data_t*)user_data;
00054 return fseek(file_data->file, position, SEEK_SET);
00055 }
00056
00057
00058
00059
00069 bool MetaIOMP4::write(Metadata* mdata, bool exclusive)
00070 {
00071 exclusive = exclusive;
00072
00073
00074 if (!mdata)
00075 return false;
00076
00077 mp4callback_data_t callback_data;
00078
00079 callback_data.fd = open(mdata->Filename().local8Bit(), O_RDWR);
00080 if (callback_data.fd < 0) {
00081 return false;
00082 }
00083
00084 callback_data.file = fdopen(callback_data.fd, "r+");
00085 if (!callback_data.file)
00086 {
00087 close(callback_data.fd);
00088 return false;
00089 }
00090
00091
00092
00093
00094
00095 mp4ff_callback_t *mp4_cb = (mp4ff_callback_t*) malloc(sizeof(mp4ff_callback_t));
00096 if (!mp4_cb) {
00097 close(callback_data.fd);
00098 fclose(callback_data.file);
00099 return false;
00100 }
00101 mp4_cb->read = md_read_callback;
00102 mp4_cb->seek = md_seek_callback;
00103 mp4_cb->write = md_write_callback;
00104 mp4_cb->truncate = md_truncate_callback;
00105 mp4_cb->user_data = &callback_data;
00106
00107 mp4ff_metadata_t *mp4ff_mdata = (mp4ff_metadata_t*)malloc(sizeof(mp4ff_metadata_t));
00108 if (!mp4ff_mdata) {
00109 free(mp4_cb);
00110 close(callback_data.fd);
00111 fclose(callback_data.file);
00112 return false;
00113 }
00114 mp4ff_mdata->tags = (mp4ff_tag_t*)malloc(7 * sizeof(mp4ff_tag_t));
00115 if (!mp4ff_mdata) {
00116 free(mp4_cb);
00117 free(mp4ff_mdata);
00118 close(callback_data.fd);
00119 fclose(callback_data.file);
00120 return false;
00121 }
00122
00123
00124
00125
00126
00127 mp4ff_t *mp4_ifile = mp4ff_open_read(mp4_cb);
00128 if (!mp4_ifile)
00129 {
00130 free(mp4_cb);
00131 free(mp4ff_mdata);
00132 close(callback_data.fd);
00133 fclose(callback_data.file);
00134 return false;
00135 }
00136
00137 mp4ff_mdata->tags[0].item = "artist";
00138 mp4ff_mdata->tags[0].value = (char*)mdata->Artist().ascii();
00139
00140 mp4ff_mdata->tags[1].item = "album";
00141 mp4ff_mdata->tags[1].value = (char*)mdata->Album().ascii();
00142
00143 mp4ff_mdata->tags[2].item = "title";
00144 mp4ff_mdata->tags[2].value = (char*)mdata->Title().ascii();
00145
00146 mp4ff_mdata->tags[3].item = "genre";
00147 mp4ff_mdata->tags[3].value = (char*)mdata->Genre().ascii();
00148
00149 mp4ff_mdata->tags[4].item = "date";
00150 mp4ff_mdata->tags[4].value = (char*)malloc(128);
00151 snprintf(mp4ff_mdata->tags[4].value, 128, "%d", mdata->Year());
00152
00153 mp4ff_mdata->tags[5].item = "track";
00154 mp4ff_mdata->tags[5].value = (char*)malloc(128);
00155 snprintf(mp4ff_mdata->tags[5].value, 128, "%d", mdata->Track());
00156
00157 mp4ff_mdata->tags[6].item = "compilation";
00158 mp4ff_mdata->tags[6].value = (char*)malloc(2);
00159 mp4ff_mdata->tags[6].value[0] = mdata->Compilation() ? 1 : 0;
00160 mp4ff_mdata->tags[6].value[1] = 0;
00161
00162 mp4ff_mdata->count = 7;
00163
00164 mp4ff_meta_update(mp4_cb, mp4ff_mdata);
00165
00166 mp4ff_close(mp4_ifile);
00167 free(mp4_cb);
00168 close(callback_data.fd);
00169 fclose(callback_data.file);
00170 free(mp4ff_mdata->tags[4].value);
00171 free(mp4ff_mdata->tags[5].value);
00172 free(mp4ff_mdata->tags[6].value);
00173 free(mp4ff_mdata->tags);
00174 free(mp4ff_mdata);
00175
00176 return true;
00177 }
00178
00179
00180
00187 Metadata* MetaIOMP4::read(QString filename)
00188 {
00189 QString artist = "", album = "", title = "", genre = "";
00190 QString writer = "", comment = "";
00191 int year = 0, tracknum = 0, length = 0;
00192 bool compilation = false;
00193
00194 mp4callback_data_t callback_data;
00195 callback_data.fd = 0;
00196 callback_data.file = fopen(filename.local8Bit(), "r");
00197 if (!callback_data.file)
00198 {
00199 return NULL;
00200 }
00201
00202
00203
00204
00205
00206
00207 mp4ff_callback_t *mp4_cb = (mp4ff_callback_t*) malloc(sizeof(mp4ff_callback_t));
00208 mp4_cb->read = md_read_callback;
00209 mp4_cb->seek = md_seek_callback;
00210 mp4_cb->user_data = &callback_data;
00211
00212
00213
00214
00215
00216
00217 mp4ff_t *mp4_ifile = mp4ff_open_read(mp4_cb);
00218 if (!mp4_ifile)
00219 {
00220 free(mp4_cb);
00221 fclose(callback_data.file);
00222 return NULL;
00223 }
00224
00225
00226
00227
00228
00229 char *char_storage = NULL;
00230
00231 if (mp4ff_meta_get_title(mp4_ifile, &char_storage))
00232 {
00233 title = QString::fromUtf8(char_storage);
00234 free(char_storage);
00235 }
00236
00237 if (mp4ff_meta_get_artist(mp4_ifile, &char_storage))
00238 {
00239 artist = QString::fromUtf8(char_storage);
00240 free(char_storage);
00241 }
00242
00243 if (mp4ff_meta_get_writer(mp4_ifile, &char_storage))
00244 {
00245 writer = QString::fromUtf8(char_storage);
00246 free(char_storage);
00247 }
00248
00249 if (mp4ff_meta_get_album(mp4_ifile, &char_storage))
00250 {
00251 album = QString::fromUtf8(char_storage);
00252 free(char_storage);
00253 }
00254
00255 if (mp4ff_meta_get_date(mp4_ifile, &char_storage))
00256 {
00257 year = QString(char_storage).toUInt();
00258 free(char_storage);
00259 }
00260
00261 if (mp4ff_meta_get_comment(mp4_ifile, &char_storage))
00262 {
00263 comment = QString::fromUtf8(char_storage);
00264 free(char_storage);
00265 }
00266
00267 if (mp4ff_meta_get_genre(mp4_ifile, &char_storage))
00268 {
00269 genre = QString::fromUtf8(char_storage);
00270 free(char_storage);
00271 }
00272
00273 if (mp4ff_meta_get_track(mp4_ifile, &char_storage))
00274 {
00275 tracknum = QString(char_storage).toUInt();
00276 free(char_storage);
00277 }
00278
00279 if (mp4ff_meta_get_compilation(mp4_ifile, &char_storage))
00280 {
00281 compilation = (1 == char_storage[0]);
00282 free(char_storage);
00283 }
00284
00285
00286
00287
00288
00289
00290 int track_num;
00291 if ( (track_num = getAACTrack(mp4_ifile)) < 0)
00292 {
00293 mp4ff_close(mp4_ifile);
00294 free(mp4_cb);
00295 fclose(callback_data.file);
00296 return NULL;
00297 }
00298
00299 unsigned char *buffer = NULL;
00300 uint buffer_size;
00301
00302 mp4ff_get_decoder_config(
00303 mp4_ifile,
00304 track_num,
00305 &buffer,
00306 &buffer_size
00307 );
00308
00309 if (!buffer)
00310 {
00311 mp4ff_close(mp4_ifile);
00312 free(mp4_cb);
00313 fclose(callback_data.file);
00314 return NULL;
00315 }
00316
00317 mp4AudioSpecificConfig mp4ASC;
00318 if (AudioSpecificConfig(buffer, buffer_size, &mp4ASC) < 0)
00319 {
00320 mp4ff_close(mp4_ifile);
00321 free(mp4_cb);
00322 fclose(callback_data.file);
00323 return NULL;
00324 }
00325
00326 long samples = mp4ff_num_samples(mp4_ifile, track_num);
00327 float f = 1024.0;
00328
00329 if (mp4ASC.sbr_present_flag == 1)
00330 {
00331 f = f * 2.0;
00332 }
00333
00334 float numb_seconds = (float)samples*(float)(f-1.0)/(float)mp4ASC.samplingFrequency;
00335
00336 length = (int) (numb_seconds * 1000);
00337
00338 mp4ff_close(mp4_ifile);
00339 free(mp4_cb);
00340 fclose(callback_data.file);
00341
00342 metadataSanityCheck(&artist, &album, &title, &genre);
00343
00344 Metadata *retdata = new Metadata(filename,
00345 artist,
00346 compilation ? artist : "",
00347 album,
00348 title,
00349 genre,
00350 year,
00351 tracknum,
00352 length);
00353
00354 retdata->setCompilation(compilation);
00355
00356
00357
00358
00359
00360 return retdata;
00361 }
00362
00363
00364
00371 int MetaIOMP4::getTrackLength(QString filename)
00372 {
00373
00374 mp4callback_data_t callback_data;
00375 callback_data.fd = 0;
00376 callback_data.file = fopen(filename.local8Bit(), "r");
00377 if (!callback_data.file)
00378 {
00379 return 0;
00380 }
00381
00382
00383
00384
00385
00386 mp4ff_callback_t *mp4_cb = (mp4ff_callback_t*) malloc(sizeof(mp4ff_callback_t));
00387 mp4_cb->read = md_read_callback;
00388 mp4_cb->seek = md_seek_callback;
00389 mp4_cb->user_data = &callback_data;
00390
00391
00392
00393
00394
00395
00396 mp4ff_t *mp4_ifile = mp4ff_open_read(mp4_cb);
00397 if (!mp4_ifile)
00398 {
00399 free(mp4_cb);
00400 fclose(callback_data.file);
00401 return 0;
00402 }
00403
00404
00405
00406
00407
00408
00409 int track_num;
00410 if ( (track_num = getAACTrack(mp4_ifile)) < 0)
00411 {
00412 mp4ff_close(mp4_ifile);
00413 free(mp4_cb);
00414 fclose(callback_data.file);
00415 return 0;
00416 }
00417
00418 unsigned char *buffer = NULL;
00419 uint buffer_size;
00420
00421 mp4ff_get_decoder_config(
00422 mp4_ifile,
00423 track_num,
00424 &buffer,
00425 &buffer_size
00426 );
00427
00428 if (!buffer)
00429 {
00430 mp4ff_close(mp4_ifile);
00431 free(mp4_cb);
00432 fclose(callback_data.file);
00433 return 0;
00434 }
00435
00436
00437 mp4AudioSpecificConfig mp4ASC;
00438 if (AudioSpecificConfig(buffer, buffer_size, &mp4ASC) < 0)
00439 {
00440 mp4ff_close(mp4_ifile);
00441 free(mp4_cb);
00442 fclose(callback_data.file);
00443 return 0;
00444 }
00445
00446 long samples = mp4ff_num_samples(mp4_ifile, track_num);
00447 float f = 1024.0;
00448
00449 if (mp4ASC.sbr_present_flag == 1)
00450 {
00451 f = f * 2.0;
00452 }
00453
00454 float numb_seconds = (float)samples*(float)(f-1.0)/(float)mp4ASC.samplingFrequency;
00455
00456 int seconds = (int) (numb_seconds * 1000);
00457
00458 mp4ff_close(mp4_ifile);
00459 free(mp4_cb);
00460 fclose(callback_data.file);
00461
00462 return seconds;
00463 }
00464
00465
00466 int MetaIOMP4::getAACTrack(mp4ff_t *infile)
00467 {
00468
00469
00470
00471
00472 int i, rc;
00473 int numTracks = mp4ff_total_tracks(infile);
00474
00475 for (i = 0; i < numTracks; i++)
00476 {
00477 unsigned char *buff = NULL;
00478 uint buff_size = 0;
00479 mp4AudioSpecificConfig mp4ASC;
00480
00481 mp4ff_get_decoder_config(infile, i, &buff, &buff_size);
00482
00483 if (buff)
00484 {
00485 rc = AudioSpecificConfig(buff, buff_size, &mp4ASC);
00486 free(buff);
00487
00488 if (rc < 0)
00489 continue;
00490 return i;
00491 }
00492 }
00493
00494
00495
00496
00497
00498 return -1;
00499 }
00500
00501
00502
00503 void MetaIOMP4::metadataSanityCheck(QString *artist, QString *album, QString *title, QString *genre)
00504 {
00505 if (artist->length() < 1)
00506 {
00507 artist->append("Unknown Artist");
00508 }
00509
00510 if (album->length() < 1)
00511 {
00512 album->append("Unknown Album");
00513 }
00514
00515 if (title->length() < 1)
00516 {
00517 title->append("Unknown Title");
00518 }
00519
00520 if (genre->length() < 1)
00521 {
00522 genre->append("Unknown Genre");
00523 }
00524
00525 }
00526
00527