Audacity  3.0.3
ImportQT.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  ImportQT.cpp
6 
7  Joshua Haberman
8 
9  On OS X, handles import of MPEG-4 audio including AAC and Apple
10  Lossless, import of audio from MPEG-4 video files, and import of
11  AIF(F)/AIFC files (AIF(F) might contain content libsndfile can't
12  handle). Could be modified to support QuickTime import on Windows.
13 
14 **********************************************************************/
15 
16 #include "../Audacity.h" // for USE_* macros
17 
18 #include "Import.h"
19 #include "ImportPlugin.h"
20 #include "../widgets/AudacityMessageBox.h"
21 #include "../widgets/ProgressDialog.h"
22 
23 #define DESC XO("QuickTime files")
24 
25 static const auto exts = {
26  wxT("aif"),
27  wxT("aifc"),
28  wxT("aiff"),
29  wxT("mov"),
30  wxT("aac"),
31  wxT("m4a"),
32  wxT("mp4")
33 };
34 
35 #if defined(__WXMAC__)
36 #undef USE_QUICKTIME
37 #endif
38 
39 #ifndef USE_QUICKTIME
40 
41 // Bug 2068: misleading error message about QuickTime
42 // In 64 bit versions we cannot compile in (obsolete) QuickTime
43 // So don't register the QuickTime extensions, so ensuring we never report
44 // "This version of Audacity was not compiled with QuickTime files support"
45 // When attempting to import MP4 files.
46 /*
47 static Importer::RegisteredUnusableImportPlugin registered{
48  std::make_unique<UnusableImportPlugin>(DESC,
49  FileExtensions( exts.begin(), exts.end() ) )
50 };
51 */
52 
53 #else /* USE_QUICKTIME */
54 
55 // There's a name collision between our Track and QuickTime's...workaround it
56 #define Track XTrack
57 
58 #if defined(__WXMAC__)
59 // These get used when building under OSX
60  #include <Carbon/Carbon.h>
61  #include <QuickTime/QuickTime.h>
62 
63  #include <wx/osx/core/private.h>
64 #else
65 // These get used when building under Windows
66  #include <ConditionalMacros.h>
67  #include <Movies.h>
68  #include <QuickTimeComponents.h>
69  #include <Sound.h>
70  #include <Folders.h>
71  #include <ToolUtils.h>
72  #include <Gestalt.h>
73  #include <Navigation.h>
74 #endif
75 
76 // There's a name collision between our Track and QuickTime's...workaround it
77 #undef Track
78 
79 #include "../Tags.h"
80 #include "../WaveTrack.h"
81 
82 #define kQTAudioPropertyID_MaxAudioSampleSize 'mssz'
83 
84 class QTImportPlugin final : public ImportPlugin
85 {
86  public:
87  QTImportPlugin()
88  : ImportPlugin( FileExtensions( exts.begin(), exts.end() ) ),
89  mInitialized(false)
90  {
91  OSErr err = noErr;
92 
93 #if defined(__WXMSW__)
94  err = ::InitializeQTML(0);
95 #endif
96 
97  if (err == noErr) {
98  err = ::EnterMovies();
99  if (err == noErr) {
100  mInitialized = true;
101  }
102  else {
103 #if defined(__WXMSW__)
104  TerminateQTML();
105 #endif
106  }
107  }
108  }
109 
110  ~QTImportPlugin()
111  {
112  if (mInitialized) {
113  ExitMovies();
114 #if defined(__WXMSW__)
115  TerminateQTML();
116 #endif
117  }
118 
119  mInitialized = false;
120  }
121 
122  wxString GetPluginStringID() override { return wxT("quicktime"); }
123 
125  std::unique_ptr<ImportFileHandle> Open(
126  const wxString & Filename, AudacityProject*) override;
127 
128  private:
129  bool mInitialized;
130 };
131 
132 class QTImportFileHandle final : public ImportFileHandle
133 {
134  public:
135  QTImportFileHandle(const wxString & name, Movie movie)
137  {
138  mMovie = movie;
139  }
140 
141  virtual ~QTImportFileHandle()
142  {
143  if (mMovie) {
144  DisposeMovie(mMovie);
145  mMovie = NULL;
146  }
147  }
148 
150  ByteCount GetFileUncompressedBytes() override;
151 
152  wxInt32 GetStreamCount() override
153  {
154  return 1;
155  }
156 
157  const TranslatableStrings &GetStreamInfo() override
158  {
159  static TranslatableStrings empty;
160  return empty;
161  }
162 
163  void SetStreamUsage(wxInt32 StreamID, bool Use) override
164  {
165  }
166 
167  ProgressResult Import(TrackFactory *trackFactory,
168  TrackHolders &outTracks,
169  Tags *tags) override;
170 
171  private:
172  void AddMetadata(Tags *tags);
173 
174  Movie mMovie;
175 };
176 
177 TranslatableString QTImportPlugin::GetPluginFormatDescription()
178 {
179  return DESC;
180 }
181 
182 std::unique_ptr<ImportFileHandle> QTImportPlugin::Open(
183  const wxString & Filename, AudacityProject*)
184 {
185  OSErr err;
186  FSRef inRef;
187  Movie theMovie = NULL;
188  Handle dataRef = NULL;
189  OSType dataRefType = 0;
190  short resID = 0;
191 
192 #if defined(__WXMAC__)
193  err = wxMacPathToFSRef(Filename, &inRef);
194 #else
195  // LLL: This will not work for pathnames with Unicode characters...find
196  // another method.
197  err = FSPathMakeRef((UInt8 *)OSFILENAME(Filename), &inRef, NULL);
198 #endif
199 
200  if (err != noErr) {
201  return nullptr;
202  }
203 
204  err = QTNewDataReferenceFromFSRef(&inRef, 0, &dataRef, &dataRefType);
205  if (err != noErr) {
206  return nullptr;
207  }
208 
209  // instantiate the movie
210  err = NewMovieFromDataRef(&theMovie,
211  newMovieActive | newMovieDontAskUnresolvedDataRefs,
212  &resID,
213  dataRef,
214  dataRefType);
215  DisposeHandle(dataRef);
216  if (err != noErr) {
217  return nullptr;
218  }
219 
220  return std::make_unique<QTImportFileHandle>(Filename, theMovie);
221 }
222 
224  std::make_unique< QTImportPlugin >()
225 };
226 
227 
228 TranslatableString QTImportFileHandle::GetFileDescription()
229 {
230  return DESC;
231 }
232 
233 auto QTImportFileHandle::GetFileUncompressedBytes() -> ByteCount
234 {
235  return 0;
236 }
237 
238 ProgressResult QTImportFileHandle::Import(TrackFactory *trackFactory,
239  TrackHolders &outTracks,
240  Tags *tags)
241 {
242  outTracks.clear();
243 
244  OSErr err = noErr;
245  MovieAudioExtractionRef maer = NULL;
246  auto updateResult = ProgressResult::Success;
247  auto totSamples =
248  (sampleCount) GetMovieDuration(mMovie); // convert from TimeValue
249  decltype(totSamples) numSamples = 0;
250  Boolean discrete = true;
251  UInt32 quality = kQTAudioRenderQuality_Max;
252  AudioStreamBasicDescription desc;
253  UInt32 maxSampleSize;
254  bool res = false;
255 
256  auto cleanup = finally( [&] {
257  if (maer) {
258  MovieAudioExtractionEnd(maer);
259  }
260  } );
261 
262  CreateProgress();
263 
264  do
265  {
266  err = MovieAudioExtractionBegin(mMovie, 0, &maer);
267  if (err != noErr) {
268  AudacityMessageBox( XO("Unable to start QuickTime extraction") );
269  break;
270  }
271 
272  err = MovieAudioExtractionSetProperty(maer,
273  kQTPropertyClass_MovieAudioExtraction_Audio,
274  kQTMovieAudioExtractionAudioPropertyID_RenderQuality,
275  sizeof(quality),
276  &quality);
277  if (err != noErr) {
278  AudacityMessageBox( XO("Unable to set QuickTime render quality") );
279  break;
280  }
281 
282  err = MovieAudioExtractionSetProperty(maer,
283  kQTPropertyClass_MovieAudioExtraction_Movie,
284  kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete,
285  sizeof(discrete),
286  &discrete);
287  if (err != noErr) {
289 "Unable to set QuickTime discrete channels property") );
290  break;
291  }
292 
293  err = MovieAudioExtractionGetProperty(maer,
294  kQTPropertyClass_MovieAudioExtraction_Audio,
295  kQTAudioPropertyID_MaxAudioSampleSize,
296  sizeof(maxSampleSize),
297  &maxSampleSize,
298  NULL);
299  if (err != noErr) {
301 "Unable to get QuickTime sample size property") );
302  break;
303  }
304 
305  err = MovieAudioExtractionGetProperty(maer,
306  kQTPropertyClass_MovieAudioExtraction_Audio,
307  kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
308  sizeof(desc),
309  &desc,
310  NULL);
311  if (err != noErr) {
312  AudacityMessageBox( XO("Unable to retrieve stream description") );
313  break;
314  }
315 
316  auto numchan = desc.mChannelsPerFrame;
317  const size_t bufsize = 5 * desc.mSampleRate;
318 
319  // determine sample format
321  switch (maxSampleSize)
322  {
323  case 16:
325  break;
326 
327  case 24:
329  break;
330 
331  default:
333  break;
334  }
335 
336  // Allocate an array of pointers, whose size is not known statically,
337  // and prefixed with the AudioBufferList structure.
339  static_cast< AudioBufferList * >(
340  calloc( 1, offsetof( AudioBufferList, mBuffers ) +
341  (sizeof(AudioBuffer) * numchan))) };
342  abl->mNumberBuffers = numchan;
343 
344  NewChannelGroup channels{ numchan };
345 
346  const auto size = sizeof(float) * bufsize;
347  ArraysOf<unsigned char> holders{ numchan, size };
348  for (size_t c = 0; c < numchan; c++) {
349  auto &buffer = abl->mBuffers[c];
350  auto &holder = holders[c];
351  auto &channel = channels[c];
352 
353  buffer.mNumberChannels = 1;
354  buffer.mDataByteSize = size;
355 
356  buffer.mData = holder.get();
357 
358  channel = trackFactory->NewWaveTrack( format );
359  channel->SetRate( desc.mSampleRate );
360  }
361 
362  do {
363  UInt32 flags = 0;
364  UInt32 numFrames = bufsize;
365 
366  err = MovieAudioExtractionFillBuffer(maer,
367  &numFrames,
368  abl.get(),
369  &flags);
370  if (err != noErr) {
371  AudacityMessageBox( XO("Unable to get fill buffer") );
372  break;
373  }
374 
375  for (size_t c = 0; c < numchan; c++) {
376  channels[c]->Append((char *) abl->mBuffers[c].mData, floatSample, numFrames);
377  }
378 
379  numSamples += numFrames;
380 
381  updateResult = mProgress->Update(
382  numSamples.as_long_long(),
383  totSamples.as_long_long() );
384 
385  if (numFrames == 0 || flags & kQTMovieAudioExtractionComplete) {
386  break;
387  }
388  } while (updateResult == ProgressResult::Success);
389 
390  res = (updateResult == ProgressResult::Success && err == noErr);
391 
392  if (res) {
393  for (auto &channel: channels)
394  channel->Flush();
395  if (!channels.empty())
396  outTracks.push_back(std::move(channels));
397  }
398 
399  //
400  // Extract any metadata
401  //
402  if (res) {
403  AddMetadata(tags);
404  }
405  } while (false);
406 
407 // done:
408 
409  return (res ? ProgressResult::Success : ProgressResult::Failed);
410 }
411 
412 static const struct
413 {
414  const OSType key;
415  const wxChar *name;
416 }
417 names[] =
418 {
419  { kQTMetaDataCommonKeyAuthor, wxT("Author") },
420  { kQTMetaDataCommonKeyComment, TAG_COMMENTS },
421  { kQTMetaDataCommonKeyCopyright, TAG_COPYRIGHT },
422  { kQTMetaDataCommonKeyDirector, wxT("Director") },
423  { kQTMetaDataCommonKeyDisplayName, wxT("Full Name") },
424  { kQTMetaDataCommonKeyInformation, wxT("Information") },
425  { kQTMetaDataCommonKeyKeywords, wxT("Keywords") },
426  { kQTMetaDataCommonKeyProducer, wxT("Producer") },
427  { kQTMetaDataCommonKeyAlbum, TAG_ALBUM },
428  { kQTMetaDataCommonKeyArtist, TAG_ARTIST },
429  { kQTMetaDataCommonKeyChapterName, wxT("Chapter") },
430  { kQTMetaDataCommonKeyComposer, wxT("Composer") },
431  { kQTMetaDataCommonKeyDescription, wxT("Description") },
432  { kQTMetaDataCommonKeyGenre, TAG_GENRE },
433  { kQTMetaDataCommonKeyOriginalFormat, wxT("Original Format") },
434  { kQTMetaDataCommonKeyOriginalSource, wxT("Original Source") },
435  { kQTMetaDataCommonKeyPerformers, wxT("Performers") },
436  { kQTMetaDataCommonKeySoftware, TAG_SOFTWARE },
437  { kQTMetaDataCommonKeyWriter, wxT("Writer") },
438 };
439 
440 void QTImportFileHandle::AddMetadata(Tags *tags)
441 {
442  QTMetaDataRef metaDataRef = NULL;
443  auto cleanup = finally( [&] {
444  // we are done so release our metadata object
445  if ( metaDataRef )
446  QTMetaDataRelease(metaDataRef);
447  } );
448 
449  OSErr err;
450 
451  err = QTCopyMovieMetaData(mMovie, &metaDataRef);
452  if (err != noErr) {
453  return;
454  }
455 
456  for (int i = 0; i < WXSIZEOF(names); i++) {
457  QTMetaDataItem item = kQTMetaDataItemUninitialized;
458  // OSType key = names[i].key;
459 
460  err = QTMetaDataGetNextItem(metaDataRef,
461  kQTMetaDataStorageFormatWildcard,
462  kQTMetaDataItemUninitialized,
463  kQTMetaDataKeyFormatCommon,
464  (const UInt8 *) &names[i].key,
465  sizeof(names[i].key),
466  &item);
467  if (err != noErr) {
468  continue;
469  }
470 
471  if (item == kQTMetaDataItemUninitialized) {
472  continue;
473  }
474 
475  QTPropertyValueType outPropType;
476  ::ByteCount outPropValueSize;
477  ::ByteCount outPropValueSizeUsed = 0;
478  UInt32 outPropFlags;
479  UInt32 dataType;
480 
481  // Get data type
482  err = QTMetaDataGetItemProperty(metaDataRef,
483  item,
484  kPropertyClass_MetaDataItem,
485  kQTMetaDataItemPropertyID_DataType,
486  sizeof(dataType),
487  &dataType,
488  &outPropValueSizeUsed);
489  if (err != noErr) {
490  continue;
491  }
492 
493  // Get the data length
494  err = QTMetaDataGetItemPropertyInfo(metaDataRef,
495  item,
496  kPropertyClass_MetaDataItem,
497  kQTMetaDataItemPropertyID_Value,
498  &outPropType,
499  &outPropValueSize,
500  &outPropFlags );
501  if (err != noErr) {
502  continue;
503  }
504 
505  // Alloc memory for it
506  ArrayOf<char> outVals{ outPropValueSize };
507 
508  // Retrieve the data
509  err = QTMetaDataGetItemProperty(metaDataRef,
510  item,
511  kPropertyClass_MetaDataItem,
512  kQTMetaDataItemPropertyID_Value,
513  outPropValueSize,
514  outVals.get(),
515  &outPropValueSizeUsed);
516  if (err != noErr)
517  continue;
518 
519  wxString v;
520 
521  switch (dataType)
522  {
523  case kQTMetaDataTypeUTF8:
524  v = wxString(outVals.get(), wxConvUTF8);
525  break;
526  case kQTMetaDataTypeUTF16BE:
527  {
528  wxMBConvUTF16BE conv;
529  v = wxString(outVals.get(), conv);
530  }
531  break;
532  }
533 
534  if (!v.empty()) {
535  tags->SetTag(names[i].name, v);
536  }
537  }
538 
539  return;
540 }
541 
542 #endif
size
size_t size
Definition: ffmpeg-2.3.6-single-header.h:412
TranslatableString
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: TranslatableString.h:32
ImportPlugin::GetPluginFormatDescription
virtual TranslatableString GetPluginFormatDescription()=0
TrackHolders
std::vector< std::vector< std::shared_ptr< WaveTrack > > > TrackHolders
Definition: Import.h:39
BasicUI::ProgressResult::Success
@ Success
OSFILENAME
#define OSFILENAME(X)
Protect against Unicode to multi-byte conversion failures on Windows.
Definition: SelectFile.h:54
AudacityMessageBox
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
Definition: AudacityMessageBox.cpp:17
TranslatableStrings
std::vector< TranslatableString > TranslatableStrings
Definition: TranslatableString.h:295
ImportPlugin
Base class for FlacImportPlugin, LOFImportPlugin, MP3ImportPlugin, OggImportPlugin and PCMImportPlugi...
Definition: ImportPlugin.h:67
Import.h
DESC
#define DESC
Definition: ImportQT.cpp:23
Tags
ID3 Tags (for MP3)
Definition: Tags.h:74
ImportFileHandle::GetFileUncompressedBytes
virtual ByteCount GetFileUncompressedBytes()=0
BasicUI::ProgressResult
ProgressResult
Definition: BasicUI.h:145
XO
#define XO(s)
Definition: Internat.h:31
int24Sample
@ int24Sample
Definition: SampleFormat.h:33
ImportFileHandle::GetStreamInfo
virtual const TranslatableStrings & GetStreamInfo()=0
TAG_SOFTWARE
#define TAG_SOFTWARE
Definition: Tags.h:67
registered
static Importer::RegisteredImportPlugin registered
Definition: ImportAUP.cpp:261
wxArrayStringEx
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
Definition: wxArrayStringEx.h:18
desc
const TranslatableString desc
Definition: ExportPCM.cpp:58
floatSample
@ floatSample
Definition: SampleFormat.h:34
Tags::SetTag
void SetTag(const wxString &name, const wxString &value, const bool bSpecialTag=false)
Definition: Tags.cpp:486
ArraysOf
memory.h template class for making an array of arrays.
Definition: MemoryX.h:81
int16Sample
@ int16Sample
Definition: SampleFormat.h:32
name
const TranslatableString name
Definition: Distortion.cpp:98
TAG_GENRE
#define TAG_GENRE
Definition: Tags.h:65
format
int format
Definition: ExportPCM.cpp:56
ImportPlugin::Open
virtual std::unique_ptr< ImportFileHandle > Open(const FilePath &Filename, AudacityProject *)=0
TAG_COPYRIGHT
#define TAG_COPYRIGHT
Definition: Tags.h:68
ImportFileHandle
An ImportFileHandle for data.
Definition: ImportPlugin.h:107
ImportFileHandle::SetStreamUsage
virtual void SetStreamUsage(wxInt32 StreamID, bool Use)=0
MallocPtr
std::unique_ptr< T, freer > MallocPtr
Definition: MemoryX.h:268
sampleFormat
sampleFormat
Definition: SampleFormat.h:29
ImportFileHandle::GetFileDescription
virtual TranslatableString GetFileDescription()=0
key
static const AudacityProject::AttachedObjects::RegisteredFactory key
Definition: CommandManager.cpp:201
exts
static const auto exts
Definition: ImportQT.cpp:25
names
static TranslatableStrings names
Definition: Tags.cpp:744
ImportFileHandle::GetStreamCount
virtual wxInt32 GetStreamCount()=0
sampleCount
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:18
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:92
ImportPlugin.h
The interface that all file import "plugins" (if you want to call them that) must implement....
Importer::RegisteredImportPlugin
Definition: Import.h:85
ImportPlugin::GetPluginStringID
virtual wxString GetPluginStringID()=0
TAG_COMMENTS
#define TAG_COMMENTS
Definition: Tags.h:66
TAG_ARTIST
#define TAG_ARTIST
Definition: Tags.h:61
NewChannelGroup
std::vector< std::shared_ptr< WaveTrack > > NewChannelGroup
Definition: Import.cpp:62
ImportFileHandle::Import
virtual ProgressResult Import(WaveTrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags)=0
ArrayOf< char >
TAG_ALBUM
#define TAG_ALBUM
Definition: Tags.h:62