Commit 60cbf80b authored by Hugo Beauzée-Luyssen's avatar Hugo Beauzée-Luyssen

Add basic audio file processing

parent b32d6371
......@@ -9,6 +9,9 @@ class IAudioTrack
virtual ~IAudioTrack() {}
virtual unsigned int id() const = 0;
virtual const std::string& codec() const = 0;
/**
* @return The bitrate in bits per second
*/
virtual unsigned int bitrate() const = 0;
virtual unsigned int sampleRate() const = 0;
virtual unsigned int nbChannels() const = 0;
......
......@@ -7,21 +7,16 @@ class IMetadataServiceCb
{
public:
virtual void updated( FilePtr file ) = 0;
virtual void error( FilePtr file, const std::string& error ) = 0;
};
class IMetadataService
{
public:
enum Result
{
Success,
Failure,
NotApplicable // If trying to fetch the tv show summary of an album, for instance
};
virtual ~IMetadataService() {}
virtual unsigned int priority() = 0;
virtual Result run( FilePtr file ) = 0;
virtual bool initialize( IMetadataServiceCb* callback, IMediaLibrary* ml ) = 0;
virtual unsigned int priority() const = 0;
virtual bool run( FilePtr file ) = 0;
};
#endif // IMETADATASERVICE_H
......@@ -32,6 +32,8 @@ list(APPEND SRC_LIST ${HEADERS_LIST}
Movie.cpp
VideoTrack.cpp
AudioTrack.cpp
metadata_services/VLCMetadataService.cpp
)
find_package(Sqlite3 REQUIRED)
......@@ -39,3 +41,7 @@ find_package(Sqlite3 REQUIRED)
add_library(${PROJECT_NAME} ${SRC_LIST})
target_link_libraries(${PROJECT_NAME} ${SQLITE3_LIBRARIES})
find_package(LIBVLC REQUIRED)
target_link_libraries( ${PROJECT_NAME} ${LIBVLC_LIBRARY} ${LIBVLCCORE_LIBRARY} )
include_directories(${LIBVLC_INCLUDE_DIR})
......@@ -64,7 +64,12 @@ FilePtr MediaLibrary::file( const std::string& path )
FilePtr MediaLibrary::addFile( const std::string& path )
{
return File::create( m_dbConnection, path );
auto file = File::create( m_dbConnection, path );
if ( file == nullptr )
return nullptr;
for ( const auto& s : m_mdServices )
s->run( file );
return file;
}
bool MediaLibrary::deleteFile( const std::string& mrl )
......
#include <iostream>
#include "VLCMetadataService.h"
#include "IFile.h"
#include "IAlbum.h"
#include "IAlbumTrack.h"
VLCMetadataService::VLCMetadataService( libvlc_instance_t* vlc )
: m_instance( vlc )
, m_cb( nullptr )
, m_ml( nullptr )
{
libvlc_retain( m_instance );
}
VLCMetadataService::~VLCMetadataService()
{
libvlc_release( m_instance );
}
bool VLCMetadataService::initialize( IMetadataServiceCb* callback, IMediaLibrary* ml )
{
m_cb = callback;
m_ml = ml;
return true;
}
unsigned int VLCMetadataService::priority() const
{
return 100;
}
bool VLCMetadataService::run( FilePtr file )
{
auto ctx = new Context;
ctx->self = this;
ctx->file = file;
ctx->media = libvlc_media_new_path( m_instance, file->mrl().c_str() );
ctx->m_em = libvlc_media_event_manager( ctx->media );
ctx->mp = libvlc_media_player_new_from_media( ctx->media );
ctx->mp_em = libvlc_media_player_event_manager( ctx->mp );
libvlc_audio_output_set( ctx->mp, "dummy" );
//attach events
libvlc_event_attach( ctx->mp_em, libvlc_MediaPlayerLengthChanged, &eventCallback, ctx );
libvlc_event_attach( ctx->m_em, libvlc_MediaParsedChanged, &eventCallback, ctx );
//libvlc_media_player_play( ctx->mp );
libvlc_media_parse_async( ctx->media );
return true;
}
void VLCMetadataService::eventCallback( const libvlc_event_t* e, void* data )
{
Context* ctx = reinterpret_cast<Context*>( data );
switch ( e->type )
{
case libvlc_MediaParsedChanged:
ctx->self->handleMediaMeta( ctx );
return;
default:
return;
}
}
void VLCMetadataService::handleMediaMeta( VLCMetadataService::Context* ctx )
{
libvlc_media_track_t** tracks;
auto nbTracks = libvlc_media_tracks_get( ctx->media, &tracks );
if ( nbTracks == 0 )
ctx->self->m_cb->error( ctx->file, "Failed to fetch tracks" );
bool isAudio = true;
for ( unsigned int i = 0; i < nbTracks; ++i )
{
std::string fcc( (const char*)(&tracks[i]->i_codec), 4 );
if ( tracks[i]->i_type != libvlc_track_audio )
{
if ( tracks[i]->i_type == libvlc_track_text )
continue ;
isAudio = false;
ctx->file->addVideoTrack( fcc, tracks[i]->video->i_width, tracks[i]->video->i_height, .0f );
}
else
{
ctx->file->addAudioTrack( fcc, tracks[i]->i_bitrate, tracks[i]->audio->i_rate,
tracks[i]->audio->i_channels );
}
}
if ( isAudio == true )
{
parseAudioFile( ctx );
}
else
{
parseVideoFile( ctx );
}
libvlc_media_tracks_release( tracks, nbTracks );
ctx->self->m_cb->updated( ctx->file );
}
void VLCMetadataService::parseAudioFile( VLCMetadataService::Context* ctx )
{
char* albumTitle = libvlc_media_get_meta( ctx->media, libvlc_meta_Album );
if ( albumTitle == nullptr )
return ;
auto album = m_ml->album( albumTitle );
if ( album == nullptr )
album = m_ml->createAlbum( albumTitle );
free( albumTitle );
if ( album == nullptr )
{
std::cerr << "Failed to create/get album" << std::endl;
return;
}
char* trackNbStr = libvlc_media_get_meta( ctx->media, libvlc_meta_TrackNumber );
if ( trackNbStr == nullptr )
{
std::cerr << "Failed to get track id" << std::endl;
return ;
}
char* trackTitle = libvlc_media_get_meta( ctx->media, libvlc_meta_Title );
std::string title;
if ( trackTitle == nullptr )
{
std::cerr << "Failed to compute track title" << std::endl;
title = "Unknown track #";
title += trackNbStr;
}
else
{
title = trackTitle;
}
unsigned int trackNb = atoi( trackNbStr );
auto track = album->addTrack( title, trackNb );
if ( track == nullptr )
{
std::cerr << "Failure while creating album track" << std::endl;
return ;
}
ctx->file->setAlbumTrack( track );
char* genre = libvlc_media_get_meta( ctx->media, libvlc_meta_Genre );
if ( genre != nullptr )
{
track->setGenre( genre );
free( genre );
}
}
void VLCMetadataService::parseVideoFile( VLCMetadataService::Context* )
{
}
VLCMetadataService::Context::Context()
: self( nullptr )
, media( nullptr )
, m_em( nullptr )
, mp( nullptr )
, mp_em( nullptr )
{
}
VLCMetadataService::Context::~Context()
{
libvlc_media_release( media );
libvlc_media_player_release( mp );
}
#ifndef VLCMETADATASERVICE_H
#define VLCMETADATASERVICE_H
#include "IMetadataService.h"
#include <vlc/vlc.h>
class VLCMetadataService : public IMetadataService
{
public:
VLCMetadataService(libvlc_instance_t* vlc);
virtual ~VLCMetadataService();
virtual bool initialize( IMetadataServiceCb *callback, IMediaLibrary* ml );
virtual unsigned int priority() const;
virtual bool run( FilePtr file );
private:
struct Context
{
Context();
~Context();
VLCMetadataService* self;
FilePtr file;
libvlc_media_t* media;
libvlc_event_manager_t* m_em;
libvlc_media_player_t* mp;
libvlc_event_manager_t* mp_em;
};
private:
static void eventCallback( const libvlc_event_t* e, void* data );
void handleMediaMeta( Context* ctx );
void parseAudioFile( Context* ctx );
void parseVideoFile( Context* ctx );
libvlc_instance_t* m_instance;
IMetadataServiceCb* m_cb;
IMediaLibrary* m_ml;
};
#endif // VLCMETADATASERVICE_H
......@@ -29,6 +29,7 @@ EnableCpp11()
add_definitions("-g")
add_definitions("-Wall -Wextra")
include_directories(${CMAKE_SOURCE_DIR}/include)
include_directories(${CMAKE_SOURCE_DIR}/src)
list(APPEND TEST_SRCS
Files.cpp
......@@ -39,6 +40,7 @@ list(APPEND TEST_SRCS
Movies.cpp
VideoTracks.cpp
AudioTracks.cpp
VLCMetadataServices.cpp
)
add_executable(unittest ${TEST_SRCS})
......
#include "gtest/gtest.h"
#include <iostream>
#include <vlc/vlc.h>
#include <condition_variable>
#include "IMediaLibrary.h"
#include "IMetadataService.h"
#include "IFile.h"
#include "IAudioTrack.h"
#include "metadata_services/VLCMetadataService.h"
class ServiceCb : public IMetadataServiceCb
{
public:
std::condition_variable waitCond;
std::mutex mutex;
ServiceCb()
{
}
virtual void updated( FilePtr )
{
waitCond.notify_all();
}
virtual void error( FilePtr, const std::string& error )
{
std::cerr << "Error: " << error << std::endl;
FAIL();
}
};
class VLCMetadataServices : public testing::Test
{
public:
static std::unique_ptr<IMediaLibrary> ml;
static std::unique_ptr<ServiceCb> cb;
static libvlc_instance_t* vlcInstance;
protected:
virtual void SetUp()
{
ml.reset( MediaLibraryFactory::create() );
cb.reset( new ServiceCb );
vlcInstance = libvlc_new( 0, NULL );
auto vlcService = new VLCMetadataService( vlcInstance );
vlcService->initialize( cb.get(), ml.get() );
ml->addMetadataService( vlcService );
bool res = ml->initialize( "test.db" );
ASSERT_TRUE( res );
}
virtual void TearDown()
{
libvlc_release( vlcInstance );
ml.reset();
unlink("test.db");
}
};
std::unique_ptr<IMediaLibrary> VLCMetadataServices::ml;
std::unique_ptr<ServiceCb> VLCMetadataServices::cb;
libvlc_instance_t* VLCMetadataServices::vlcInstance;
TEST_F( VLCMetadataServices, ParseAudio )
{
std::unique_lock<std::mutex> lock( cb->mutex );
auto file = ml->addFile( "/home/chouquette/samples/mr-zebra.mp3" );
std::vector<AudioTrackPtr> tracks;
cb->waitCond.wait( lock, [&]{ return file->audioTracks( tracks ) == true && tracks.size() > 0; } );
SetUp();
file = ml->file( "/home/chouquette/samples/mr-zebra.mp3" );
bool res = file->audioTracks( tracks );
ASSERT_TRUE( res );
ASSERT_EQ( tracks.size(), 1u );
auto track = tracks[0];
ASSERT_EQ( track->codec(), "mpga" );
ASSERT_EQ( track->bitrate(), 128000 );
ASSERT_EQ( track->sampleRate(), 44100 );
ASSERT_EQ( track->nbChannels(), 2 );
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment