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

VLCThumbnailer: Use vmem to compute snapshots

parent 349b20ca
......@@ -97,8 +97,12 @@ target_link_libraries(${PROJECT_NAME} ${SQLITE3_LIBRARIES})
if (${WITH_VLC})
find_package(LIBVLC REQUIRED)
target_link_libraries( ${PROJECT_NAME} ${LIBVLC_LIBRARY})
# Since VLC include a thumbnailer, we need libjpeg to encode the result
find_package(JPEG REQUIRED)
target_link_libraries( ${PROJECT_NAME} ${LIBVLC_LIBRARY} )
target_link_libraries( ${PROJECT_NAME} ${JPEG_LIBRARIES} )
include_directories(${LIBVLC_INCLUDE_DIR})
include_directories(${JPEG_INCLUDE_DIR})
set(LIBVLCPP_DIR "" CACHE PATH "Path to libvlcpp's directory")
include_directories(${LIBVLCPP_DIR})
endif()
......
......@@ -81,7 +81,6 @@ bool MediaLibrary::initialize( const std::string& dbPath, const std::string& sna
{
const char* args[] = {
"-vv",
"--vout=dummy",
};
m_vlcInstance = VLC::Instance( sizeof(args) / sizeof(args[0]), args );
m_vlcInstance.logSet([](int lvl, const libvlc_log_t*, std::string msg) {
......
#include "VLCThumbnailer.h"
#include <cstring>
#include <jpeglib.h>
#include "IFile.h"
#include "logging/Logger.h"
VLCThumbnailer::VLCThumbnailer(const VLC::Instance &vlc)
: m_instance( vlc )
, m_snapshotRequired( false )
, m_height( 0 )
{
}
......@@ -41,7 +45,6 @@ bool VLCThumbnailer::run( FilePtr file, void *data )
VLC::Media media( m_instance, file->mrl(), VLC::Media::FromPath );
media.addOption( ":no-audio" );
media.addOption( ":avcodec-hw=none" );
VLC::MediaPlayer mp( media );
setupVout( mp );
......@@ -53,32 +56,20 @@ bool VLCThumbnailer::run( FilePtr file, void *data )
if ( seekAhead( file, mp, data ) == false )
return false;
auto path = m_ml->snapshotPath();
path += "/";
path += std::to_string( file->id() ) + ".jpg";
if ( mp.takeSnapshot( 0, path, 320, 240 ) == false )
{
m_cb->done( file, StatusError, data );
if ( takeSnapshot( file, mp, data ) == false )
return false;
}
m_cb->done( file, StatusSuccess, data );
file->setSnapshot( path );
return true;
}
bool VLCThumbnailer::startPlayback( FilePtr file, VLC::MediaPlayer &mp, void* data )
{
bool failed = true;
int nbVout = 0;
std::unique_lock<std::mutex> lock( m_mutex );
// We need a valid vout to call takeSnapshot. Let's also
// assume the media player is playing when a vout is created.
mp.eventManager().onVout([this, &failed, &nbVout](int nb) {
mp.eventManager().onPlaying([this, &failed]() {
std::unique_lock<std::mutex> lock( m_mutex );
failed = false;
nbVout = nb;
m_cond.notify_all();
});
mp.eventManager().onEncounteredError([this]() {
......@@ -86,9 +77,8 @@ bool VLCThumbnailer::startPlayback( FilePtr file, VLC::MediaPlayer &mp, void* da
m_cond.notify_all();
});
mp.play();
bool success = m_cond.wait_for( lock, std::chrono::seconds( 3 ), [&mp, &nbVout]() {
return (mp.state() == libvlc_Playing && nbVout > 0) ||
mp.state() == libvlc_Error;
bool success = m_cond.wait_for( lock, std::chrono::seconds( 3 ), [&mp]() {
return mp.state() == libvlc_Playing || mp.state() == libvlc_Error;
});
if ( success == false || failed == true )
{
......@@ -108,7 +98,7 @@ bool VLCThumbnailer::seekAhead( FilePtr file, VLC::MediaPlayer& mp, void* data )
pos = p;
m_cond.notify_all();
});
mp.setPosition( .3f );
mp.setPosition( .4f );
bool success = m_cond.wait_for( lock, std::chrono::seconds( 3 ), [&pos]() {
return pos >= .1f;
});
......@@ -122,32 +112,118 @@ bool VLCThumbnailer::seekAhead( FilePtr file, VLC::MediaPlayer& mp, void* data )
return true;
}
void VLCThumbnailer::setupVout( VLC::MediaPlayer& )
void VLCThumbnailer::setupVout( VLC::MediaPlayer& mp )
{
mp.setVideoFormatCallbacks(
// Setup
[this, &mp](char* chroma, unsigned int* width, unsigned int *height, unsigned int *pitches, unsigned int *lines) {
strcpy( chroma, "RV32" );
const float inputAR = (float)*width / *height;
*width = Width;
auto prevHeight = m_height;
m_height = (float)Width / inputAR + 1;
// If our buffer isn't enough anymore, reallocate a new one.
if ( m_height > prevHeight )
m_buff.reset( new uint8_t[Width * m_height * Bpp] );
*height = m_height;
*pitches = Width * Bpp;
*lines = m_height;
return 1;
},
// Cleanup
nullptr);
mp.setVideoCallbacks(
// Lock
[this](void** pp_buff) {
*pp_buff = m_buff.get();
return nullptr;
},
//unlock
[this](void*, void*const*) {
std::unique_lock<std::mutex> lock(m_mutex);
if ( m_snapshotRequired == true )
{
m_snapshotRequired = false;
m_cond.notify_all();
}
}
,
//display
nullptr
);
}
bool VLCThumbnailer::takeSnapshot(FilePtr file, VLC::MediaPlayer &mp, void *data)
{
return;
// Let's use this ugly base in the future, but so far, takeSnapshot() will do.
// if ( m_buff == nullptr )
// m_buff.reset( new uint8_t[320 * 240 * 4] );
// mp.setVideoFormatCallbacks(
// // Setup
// [](char* chroma, unsigned int* width, unsigned int *height, unsigned int *pitches, unsigned int *lines) {
// strcpy( chroma, "RV32" );
// *width = 320;
// *height = 240;
// *pitches = 240 * 4;
// *lines = 240;
// return 1;
// },
// // Cleanup
// nullptr);
// mp.setVideoCallbacks(
// // Lock
// [this](void** pp_buff) {
// *pp_buff = m_buff.get();
// return nullptr;
// },
// //unlock, display
// nullptr, nullptr);
std::unique_ptr<uint8_t[]> buff;
// lock, signal that we want a snapshot, and wait.
{
std::unique_lock<std::mutex> lock( m_mutex );
m_snapshotRequired = true;
bool success = m_cond.wait_for( lock, std::chrono::seconds( 3 ), [this]() {
// Keep waiting if the vmem thread hasn't restored m_snapshotRequired to false
return m_snapshotRequired == true;
});
if ( success == false )
{
m_cb->done( file, StatusError, data );
return false;
}
// Prevent the vmem from screwing our snapshot over.
buff = std::move( m_buff );
// set a new buffer for next snapshot, and to avoid crashing while we stop the media player
m_buff.reset( new uint8_t[Width * m_height * Bpp] );
}
mp.stop();
return compress( buff.get(), file, data );
}
bool VLCThumbnailer::compress(uint8_t* buff, FilePtr file, void *data)
{
jpeg_compress_struct compInfo;
jpeg_error_mgr jerr;
JSAMPROW row_pointer[1];
auto path = m_ml->snapshotPath();
path += "/";
path += std::to_string( file->id() ) + ".jpg";
compInfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&compInfo);
//FIXME: Abstract this away, though libjpeg requires a FILE*...
auto fOut = fopen(path.c_str(), "wb");
// ensure we always close the file.
auto fOutPtr = std::unique_ptr<FILE, int(*)(FILE*)>( fOut, &fclose );
if ( fOut == nullptr )
{
LOG_ERROR("Failed to open snapshot file ", path);
m_cb->done( file, StatusError, data );
return false;
}
jpeg_stdio_dest(&compInfo, fOut);
compInfo.image_width = Width;
compInfo.image_height = m_height;
compInfo.input_components = Bpp;
compInfo.in_color_space = JCS_EXT_BGRX;
jpeg_set_defaults( &compInfo );
jpeg_set_quality( &compInfo, 85, TRUE );
jpeg_start_compress( &compInfo, TRUE );
auto stride = compInfo.image_width * Bpp;
while (compInfo.next_scanline < compInfo.image_height) {
row_pointer[0] = &buff[compInfo.next_scanline * stride];
jpeg_write_scanlines(&compInfo, row_pointer, 1);
}
jpeg_finish_compress(&compInfo);
jpeg_destroy_compress(&compInfo);
m_cb->done( file, StatusSuccess, data );
file->setSnapshot( path );
return true;
}
......@@ -18,6 +18,13 @@ private:
bool startPlayback(FilePtr file, VLC::MediaPlayer& mp , void *data);
bool seekAhead(FilePtr file, VLC::MediaPlayer &mp, void *data);
void setupVout(VLC::MediaPlayer &mp);
bool takeSnapshot(FilePtr file, VLC::MediaPlayer &mp, void* data);
bool compress(uint8_t* buff, FilePtr file, void* data );
private:
// Force a base width, let height be computed depending on A/R
static const uint32_t Width = 320;
static const uint8_t Bpp = 4;
private:
VLC::Instance m_instance;
......@@ -25,5 +32,8 @@ private:
IMediaLibrary* m_ml;
std::mutex m_mutex;
std::condition_variable m_cond;
// Per snapshot variables
std::unique_ptr<uint8_t[]> m_buff;
bool m_snapshotRequired;
uint32_t m_height;
};
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