
Recherche avancée
Médias (91)
-
GetID3 - Boutons supplémentaires
9 avril 2013, par
Mis à jour : Avril 2013
Langue : français
Type : Image
-
Core Media Video
4 avril 2013, par
Mis à jour : Juin 2013
Langue : français
Type : Video
-
The pirate bay depuis la Belgique
1er avril 2013, par
Mis à jour : Avril 2013
Langue : français
Type : Image
-
Bug de détection d’ogg
22 mars 2013, par
Mis à jour : Avril 2013
Langue : français
Type : Video
-
Exemple de boutons d’action pour une collection collaborative
27 février 2013, par
Mis à jour : Mars 2013
Langue : français
Type : Image
-
Exemple de boutons d’action pour une collection personnelle
27 février 2013, par
Mis à jour : Février 2013
Langue : English
Type : Image
Autres articles (104)
-
MediaSPIP 0.1 Beta version
25 avril 2011, parMediaSPIP 0.1 beta is the first version of MediaSPIP proclaimed as "usable".
The zip file provided here only contains the sources of MediaSPIP in its standalone version.
To get a working installation, you must manually install all-software dependencies on the server.
If you want to use this archive for an installation in "farm mode", you will also need to proceed to other manual (...) -
MediaSPIP version 0.1 Beta
16 avril 2011, parMediaSPIP 0.1 beta est la première version de MediaSPIP décrétée comme "utilisable".
Le fichier zip ici présent contient uniquement les sources de MediaSPIP en version standalone.
Pour avoir une installation fonctionnelle, il est nécessaire d’installer manuellement l’ensemble des dépendances logicielles sur le serveur.
Si vous souhaitez utiliser cette archive pour une installation en mode ferme, il vous faudra également procéder à d’autres modifications (...) -
Amélioration de la version de base
13 septembre 2013Jolie sélection multiple
Le plugin Chosen permet d’améliorer l’ergonomie des champs de sélection multiple. Voir les deux images suivantes pour comparer.
Il suffit pour cela d’activer le plugin Chosen (Configuration générale du site > Gestion des plugins), puis de configurer le plugin (Les squelettes > Chosen) en activant l’utilisation de Chosen dans le site public et en spécifiant les éléments de formulaires à améliorer, par exemple select[multiple] pour les listes à sélection multiple (...)
Sur d’autres sites (10764)
-
Problems with Streaming a Multicast RTSP Stream with Live555
16 juin 2014, par ALM865I am having trouble setting up a Multicast RTSP session using Live555. The examples included with Live555 are mostly irrelevant as they deal with reading in files and my code differs because it reads in encoded frames generated from a FFMPEG thread within my own program (no pipes, no saving to disk, it is genuinely passing pointers to memory that contain the encoded frames for Live555 to stream).
My Live555 project that uses a custom Server Media Subsession so that I can receive data from an FFMPEG thread within my program (instead of Live555’s default reading from a file, yuk !). This is a requirement of my program as it reads in a GigEVision stream in one thread, sends the decoded raw RGB packets to the FFMPEG thread, which then in turn sends the encoded frames off to Live555 for RTSP streaming.
For the life of me I can’t work out how to send the RTSP stream as multicast instead of unicast !
Just a note, my program works perfectly at the moment streaming Unicast, so there is nothing wrong with my Live555 implementation (before you go crazy picking out irrelevant errors !). I just need to know how to modify my existing code to stream Multicast instead of Unicast.
My program is way too big to upload and share so I’m just going to share the important bits :
Live_AnalysingServerMediaSubsession.h
#ifndef _ANALYSING_SERVER_MEDIA_SUBSESSION_HH
#define _ANALYSING_SERVER_MEDIA_SUBSESSION_HH
#include
#include "Live_AnalyserInput.h"
class AnalysingServerMediaSubsession: public OnDemandServerMediaSubsession {
public:
static AnalysingServerMediaSubsession*
createNew(UsageEnvironment& env, AnalyserInput& analyserInput, unsigned estimatedBitrate,
Boolean iFramesOnly = False,
double vshPeriod = 5.0
/* how often (in seconds) to inject a Video_Sequence_Header,
if one doesn't already appear in the stream */);
protected: // we're a virtual base class
AnalysingServerMediaSubsession(UsageEnvironment& env, AnalyserInput& AnalyserInput, unsigned estimatedBitrate, Boolean iFramesOnly, double vshPeriod);
virtual ~AnalysingServerMediaSubsession();
protected:
AnalyserInput& fAnalyserInput;
unsigned fEstimatedKbps;
private:
Boolean fIFramesOnly;
double fVSHPeriod;
// redefined virtual functions
virtual FramedSource* createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate);
virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* inputSource);
};
#endifAnd "Live_AnalysingServerMediaSubsession.cpp"
#include "Live_AnalysingServerMediaSubsession.h"
#include
#include
#include
AnalysingServerMediaSubsession* AnalysingServerMediaSubsession::createNew(UsageEnvironment& env, AnalyserInput& wisInput, unsigned estimatedBitrate,
Boolean iFramesOnly,
double vshPeriod) {
return new AnalysingServerMediaSubsession(env, wisInput, estimatedBitrate,
iFramesOnly, vshPeriod);
}
AnalysingServerMediaSubsession
::AnalysingServerMediaSubsession(UsageEnvironment& env, AnalyserInput& analyserInput, unsigned estimatedBitrate, Boolean iFramesOnly, double vshPeriod)
: OnDemandServerMediaSubsession(env, True /*reuse the first source*/),
fAnalyserInput(analyserInput), fIFramesOnly(iFramesOnly), fVSHPeriod(vshPeriod) {
fEstimatedKbps = (estimatedBitrate + 500)/1000;
}
AnalysingServerMediaSubsession
::~AnalysingServerMediaSubsession() {
}
FramedSource* AnalysingServerMediaSubsession ::createNewStreamSource(unsigned /*clientSessionId*/, unsigned& estBitrate) {
estBitrate = fEstimatedKbps;
// Create a framer for the Video Elementary Stream:
//LOG_MSG("Create Net Stream Source [%d]", estBitrate);
return MPEG1or2VideoStreamDiscreteFramer::createNew(envir(), fAnalyserInput.videoSource());
}
RTPSink* AnalysingServerMediaSubsession ::createNewRTPSink(Groupsock* rtpGroupsock, unsigned char /*rtpPayloadTypeIfDynamic*/, FramedSource* /*inputSource*/) {
setVideoRTPSinkBufferSize();
/*
struct in_addr destinationAddress;
destinationAddress.s_addr = inet_addr("239.255.12.42");
rtpGroupsock->addDestination(destinationAddress,8888);
rtpGroupsock->multicastSendOnly();
*/
return MPEG1or2VideoRTPSink::createNew(envir(), rtpGroupsock);
}Live_AnalyserSouce.h
#ifndef _ANALYSER_SOURCE_HH
#define _ANALYSER_SOURCE_HH
#ifndef _FRAMED_SOURCE_HH
#include "FramedSource.hh"
#endif
class FFMPEG;
// The following class can be used to define specific encoder parameters
class AnalyserParameters {
public:
FFMPEG * Encoding_Source;
};
class AnalyserSource: public FramedSource {
public:
static AnalyserSource* createNew(UsageEnvironment& env, FFMPEG * E_Source);
static unsigned GetRefCount();
public:
static EventTriggerId eventTriggerId;
protected:
AnalyserSource(UsageEnvironment& env, FFMPEG * E_Source);
// called only by createNew(), or by subclass constructors
virtual ~AnalyserSource();
private:
// redefined virtual functions:
virtual void doGetNextFrame();
private:
static void deliverFrame0(void* clientData);
void deliverFrame();
private:
static unsigned referenceCount; // used to count how many instances of this class currently exist
FFMPEG * Encoding_Source;
unsigned int Last_Sent_Frame_ID;
};
#endifLive_AnalyserSource.cpp
#include "Live_AnalyserSource.h"
#include // for "gettimeofday()"
#include "FFMPEGClass.h"
AnalyserSource* AnalyserSource::createNew(UsageEnvironment& env, FFMPEG * E_Source) {
return new AnalyserSource(env, E_Source);
}
EventTriggerId AnalyserSource::eventTriggerId = 0;
unsigned AnalyserSource::referenceCount = 0;
AnalyserSource::AnalyserSource(UsageEnvironment& env, FFMPEG * E_Source) : FramedSource(env), Encoding_Source(E_Source) {
if (referenceCount == 0) {
// Any global initialization of the device would be done here:
}
++referenceCount;
// Any instance-specific initialization of the device would be done here:
Last_Sent_Frame_ID = 0;
/* register us with the Encoding thread so we'll get notices when new frame data turns up.. */
Encoding_Source->RegisterRTSP_Source(&(env.taskScheduler()), this);
// We arrange here for our "deliverFrame" member function to be called
// whenever the next frame of data becomes available from the device.
//
// If the device can be accessed as a readable socket, then one easy way to do this is using a call to
// envir().taskScheduler().turnOnBackgroundReadHandling( ... )
// (See examples of this call in the "liveMedia" directory.)
//
// If, however, the device *cannot* be accessed as a readable socket, then instead we can implement is using 'event triggers':
// Create an 'event trigger' for this device (if it hasn't already been done):
if (eventTriggerId == 0) {
eventTriggerId = envir().taskScheduler().createEventTrigger(deliverFrame0);
}
}
AnalyserSource::~AnalyserSource() {
// Any instance-specific 'destruction' (i.e., resetting) of the device would be done here:
/* de-register this source from the Encoding thread, since we no longer need notices.. */
Encoding_Source->Un_RegisterRTSP_Source(this);
--referenceCount;
if (referenceCount == 0) {
// Any global 'destruction' (i.e., resetting) of the device would be done here:
// Reclaim our 'event trigger'
envir().taskScheduler().deleteEventTrigger(eventTriggerId);
eventTriggerId = 0;
}
}
unsigned AnalyserSource::GetRefCount() {
return referenceCount;
}
void AnalyserSource::doGetNextFrame() {
// This function is called (by our 'downstream' object) when it asks for new data.
//LOG_MSG("Do Next Frame..");
// Note: If, for some reason, the source device stops being readable (e.g., it gets closed), then you do the following:
//if (0 /* the source stops being readable */ /*%%% TO BE WRITTEN %%%*/) {
unsigned int FrameID = Encoding_Source->GetFrameID();
if (FrameID == 0){
//LOG_MSG("No Data. Close");
handleClosure(this);
return;
}
// If a new frame of data is immediately available to be delivered, then do this now:
if (Last_Sent_Frame_ID != FrameID){
deliverFrame();
//DEBUG_MSG("Frame ID: %d",FrameID);
}
// No new data is immediately available to be delivered. We don't do anything more here.
// Instead, our event trigger must be called (e.g., from a separate thread) when new data becomes available.
}
void AnalyserSource::deliverFrame0(void* clientData) {
((AnalyserSource*)clientData)->deliverFrame();
}
void AnalyserSource::deliverFrame() {
if (!isCurrentlyAwaitingData()) return; // we're not ready for the data yet
static u_int8_t* newFrameDataStart;
static unsigned newFrameSize = 0;
/* get the data frame from the Encoding thread.. */
if (Encoding_Source->GetFrame(&newFrameDataStart, &newFrameSize, &Last_Sent_Frame_ID)){
if (newFrameDataStart!=NULL) {
/* This should never happen, but check anyway.. */
if (newFrameSize > fMaxSize) {
fFrameSize = fMaxSize;
fNumTruncatedBytes = newFrameSize - fMaxSize;
} else {
fFrameSize = newFrameSize;
}
gettimeofday(&fPresentationTime, NULL); // If you have a more accurate time - e.g., from an encoder - then use that instead.
// If the device is *not* a 'live source' (e.g., it comes instead from a file or buffer), then set "fDurationInMicroseconds" here.
/* move the data to be sent off.. */
memmove(fTo, newFrameDataStart, fFrameSize);
/* release the Mutex we had on the Frame's buffer.. */
Encoding_Source->ReleaseFrame();
}
else {
//AM Added, something bad happened
//ALTRACE("LIVE555: FRAME NULL\n");
fFrameSize=0;
fTo=NULL;
handleClosure(this);
}
}
else {
//LOG_MSG("Closing Connection due to Frame Error..");
handleClosure(this);
}
// After delivering the data, inform the reader that it is now available:
FramedSource::afterGetting(this);
}Live_AnalyserInput.cpp
#include "Live_AnalyserInput.h"
#include "Live_AnalyserSource.h"
////////// WISInput implementation //////////
AnalyserInput* AnalyserInput::createNew(UsageEnvironment& env, FFMPEG *Encoder) {
if (!fHaveInitialized) {
//if (!initialize(env)) return NULL;
fHaveInitialized = True;
}
return new AnalyserInput(env, Encoder);
}
FramedSource* AnalyserInput::videoSource() {
if (fOurVideoSource == NULL || AnalyserSource::GetRefCount() == 0) {
fOurVideoSource = AnalyserSource::createNew(envir(), m_Encoder);
}
return fOurVideoSource;
}
AnalyserInput::AnalyserInput(UsageEnvironment& env, FFMPEG *Encoder): Medium(env), m_Encoder(Encoder) {
}
AnalyserInput::~AnalyserInput() {
/* When we get destroyed, make sure our source is also destroyed.. */
if (fOurVideoSource != NULL && AnalyserSource::GetRefCount() != 0) {
AnalyserSource::handleClosure(fOurVideoSource);
}
}
Boolean AnalyserInput::fHaveInitialized = False;
int AnalyserInput::fOurVideoFileNo = -1;
FramedSource* AnalyserInput::fOurVideoSource = NULL;Live_AnalyserInput.h
#ifndef _ANALYSER_INPUT_HH
#define _ANALYSER_INPUT_HH
#include
#include "FFMPEGClass.h"
class AnalyserInput: public Medium {
public:
static AnalyserInput* createNew(UsageEnvironment& env, FFMPEG *Encoder);
FramedSource* videoSource();
private:
AnalyserInput(UsageEnvironment& env, FFMPEG *Encoder); // called only by createNew()
virtual ~AnalyserInput();
private:
friend class WISVideoOpenFileSource;
static Boolean fHaveInitialized;
static int fOurVideoFileNo;
static FramedSource* fOurVideoSource;
FFMPEG *m_Encoder;
};
// Functions to set the optimal buffer size for RTP sink objects.
// These should be called before each RTPSink is created.
#define VIDEO_MAX_FRAME_SIZE 300000
inline void setVideoRTPSinkBufferSize() { OutPacketBuffer::maxSize = VIDEO_MAX_FRAME_SIZE; }
#endifAnd finally the relevant code from my Live555 worker thread that starts the whole process :
Stop_RTSP_Loop=0;
// MediaSession *ms;
TaskScheduler *scheduler;
UsageEnvironment *env ;
// RTSPClient *rtsp;
// MediaSubsession *Video_Sub;
char RTSP_Address[1024];
RTSP_Address[0]=0x00;
if (m_Encoder == NULL){
//DEBUG_MSG("No Video Encoder registered for the RTSP Encoder");
return 0;
}
scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
UserAuthenticationDatabase* authDB = NULL;
#ifdef ACCESS_CONTROL
// To implement client access control to the RTSP server, do the following:
if (m_Enable_Pass){
authDB = new UserAuthenticationDatabase;
authDB->addUserRecord(UserN, PassW);
}
////////// authDB = new UserAuthenticationDatabase;
////////// authDB->addUserRecord((char*)"Admin", (char*)"Admin"); // replace these with real strings
// Repeat the above with each <username>, <password> that you wish to allow
// access to the server.
#endif
// Create the RTSP server:
RTSPServer* rtspServer = RTSPServer::createNew(*env, 554, authDB);
ServerMediaSession* sms;
AnalyserInput* inputDevice;
if (rtspServer == NULL) {
TRACE("LIVE555: Failed to create RTSP server: %s\n", env->getResultMsg());
return 0;
}
else {
char const* descriptionString = "Session streamed by \"IMC Server\"";
// Initialize the WIS input device:
inputDevice = AnalyserInput::createNew(*env, m_Encoder);
if (inputDevice == NULL) {
TRACE("Live555: Failed to create WIS input device\n");
return 0;
}
else {
// A MPEG-1 or 2 video elementary stream:
/* Increase the buffer size so we can handle the high res stream.. */
OutPacketBuffer::maxSize = 300000;
// NOTE: This *must* be a Video Elementary Stream; not a Program Stream
sms = ServerMediaSession::createNew(*env, RTSP_Address, RTSP_Address, descriptionString);
//sms->addSubsession(MPEG1or2VideoFileServerMediaSubsession::createNew(*env, inputFileName, reuseFirstSource, iFramesOnly));
sms->addSubsession(AnalysingServerMediaSubsession::createNew(*env, *inputDevice, m_Encoder->Get_Bitrate()));
//sms->addSubsession(WISMPEG1or2VideoServerMediaSubsession::createNew(sms->envir(), inputDevice, videoBitrate));
rtspServer->addServerMediaSession(sms);
//announceStream(rtspServer, sms, streamName, inputFileName);
//LOG_MSG("Play this stream using the URL %s", rtspServer->rtspURL(sms));
}
}
Stop_RTSP_Loop=0;
for (;;)
{
/* The actual work is all carried out inside the LIVE555 Task scheduler */
env->taskScheduler().doEventLoop(&Stop_RTSP_Loop); // does not return
if (mStop) {
break;
}
}
Medium::close(rtspServer); // will also reclaim "sms" and its "ServerMediaSubsession"s
Medium::close(inputDevice);
</password></username> -
Transcode of H.264 to VP8 using libav* has incorrect frame rate
17 avril 2014, par Kevin WatsonI’ve so far failed to get the correct output frame rate when transcoding H.264 to VP8 with the libav* libraries. I created a functioning encode of Sintel.2010.720p.mkv as WebM (VP8/Vorbis) using a modification of the transcoding.c example in the FFmpeg source. Unfortunately the resulting file is 48 fps unlike the 24 fps of the original and the output of the ffmpeg command I’m trying to mimic.
I noticed ffprobe produces a tbc of double the fps for this and other H.264 videos, while the tbc of the resulting VP8 stream produced by the ffmpeg command is the default 1000. The stock transcoding.c example copies the time base of the decoder to the encoder AVCodecContext, which is 1/48. Running the ffmpeg command through gdb it looks like the time base of the AVCodecContext is set to 1/24, but making that change alone only causes the resulting video to be slowed to twice the duration at 24 fps.
I can create a usable video, but the frame rate doubles. When the output frame rate is the correct 24 fps, the video is smooth but slowed to half speed.
Here is my modification of the example.
/*
* Copyright (c) 2010 Nicolas George
* Copyright (c) 2011 Stefano Sabatini
* Copyright (c) 2014 Andrey Utkin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* @file
* API example for demuxing, decoding, filtering, encoding and muxing
* @example doc/examples/transcoding.c
*/
#include <libavcodec></libavcodec>avcodec.h>
#include <libavformat></libavformat>avformat.h>
#include <libavfilter></libavfilter>avfiltergraph.h>
#include <libavfilter></libavfilter>avcodec.h>
#include <libavfilter></libavfilter>buffersink.h>
#include <libavfilter></libavfilter>buffersrc.h>
#include <libavutil></libavutil>opt.h>
#include <libavutil></libavutil>pixdesc.h>
#define STATS_LOG "stats.log"
static AVFormatContext *ifmt_ctx;
static AVFormatContext *ofmt_ctx;
typedef struct FilteringContext {
AVFilterContext *buffersink_ctx;
AVFilterContext *buffersrc_ctx;
AVFilterGraph *filter_graph;
} FilteringContext;
static FilteringContext *filter_ctx;
static int open_input_file(const char *filename) {
int ret;
unsigned int i;
ifmt_ctx = NULL;
if ((ret = avformat_open_input(&ifmt_ctx, filename, NULL, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n");
return ret;
}
if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");
return ret;
}
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
AVStream *stream;
AVCodecContext *codec_ctx;
stream = ifmt_ctx->streams[i];
codec_ctx = stream->codec;
/* Reencode video & audio and remux subtitles etc. */
if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO
|| codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
/* Open decoder */
ret = avcodec_open2(codec_ctx,
avcodec_find_decoder(codec_ctx->codec_id), NULL);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Failed to open decoder for stream #%u\n", i);
return ret;
}
}
}
av_dump_format(ifmt_ctx, 0, filename, 0);
return 0;
}
static int init_output_context(char* filename) {
int ret;
ofmt_ctx = NULL;
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, filename);
if (!ofmt_ctx) {
av_log(NULL, AV_LOG_ERROR, "Could not create output context\n");
return AVERROR_UNKNOWN;
}
return 0;
}
static int init_webm_encoders(int audioBitRate, int crf, int videoMaxBitRate, int threads,
char* quality, int speed, int pass, char* stats) {
AVStream *out_stream;
AVStream *in_stream;
AVCodecContext *dec_ctx, *enc_ctx;
AVCodec *encoder;
int ret;
unsigned int i;
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
in_stream = ifmt_ctx->streams[i];
dec_ctx = in_stream->codec;
if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO || dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
AVDictionary *opts = NULL;
if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
encoder = avcodec_find_encoder(AV_CODEC_ID_VP8);
out_stream = avformat_new_stream(ofmt_ctx, encoder);
if (!out_stream) {
av_log(NULL, AV_LOG_ERROR, "Failed allocating output stream\n");
return AVERROR_UNKNOWN;
}
enc_ctx = out_stream->codec;
enc_ctx->height = dec_ctx->height;
enc_ctx->width = dec_ctx->width;
enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;
/* take first format from list of supported formats */
enc_ctx->pix_fmt = encoder->pix_fmts[0];
/* video time_base can be set to whatever is handy and supported by encoder */
enc_ctx->time_base = dec_ctx->time_base;
/* enc_ctx->time_base.num = 1; */
/* enc_ctx->time_base.den = 24; */
enc_ctx->bit_rate = videoMaxBitRate;
enc_ctx->thread_count = threads;
switch (pass) {
case 1:
enc_ctx->flags |= CODEC_FLAG_PASS1;
break;
case 2:
enc_ctx->flags |= CODEC_FLAG_PASS2;
if (stats) {
enc_ctx->stats_in = stats;
}
break;
}
char crfString[3];
snprintf(crfString, 3, "%d", crf);
av_dict_set(&opts, "crf", crfString, 0);
av_dict_set(&opts, "quality", quality, 0);
char speedString[3];
snprintf(speedString, 3, "%d", speed);
av_dict_set(&opts, "speed", speedString, 0);
} else {
encoder = avcodec_find_encoder(AV_CODEC_ID_VORBIS);
out_stream = avformat_new_stream(ofmt_ctx, encoder);
if (!out_stream) {
av_log(NULL, AV_LOG_ERROR, "Failed allocating output stream\n");
return AVERROR_UNKNOWN;
}
/* in_stream = ifmt_ctx->streams[i]; */
/* dec_ctx = in_stream->codec; */
enc_ctx = out_stream->codec;
/* encoder = out_stream->codec->codec; */
enc_ctx->sample_rate = dec_ctx->sample_rate;
enc_ctx->channel_layout = dec_ctx->channel_layout;
enc_ctx->channels = av_get_channel_layout_nb_channels(enc_ctx->channel_layout);
/* take first format from list of supported formats */
enc_ctx->sample_fmt = encoder->sample_fmts[0];
enc_ctx->time_base = (AVRational){1, enc_ctx->sample_rate};
enc_ctx->bit_rate = audioBitRate;
}
/* Open codec with the set options */
ret = avcodec_open2(enc_ctx, encoder, &opts);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot open video encoder for stream #%u\n", i);
return ret;
}
int unused = av_dict_count(opts);
if (unused > 0) {
av_log(NULL, AV_LOG_WARNING, "%d unused options\n", unused);
}
/* } else if (dec_ctx->codec_type == AVMEDIA_TYPE_UNKNOWN) { */
} else {
av_log(NULL, AV_LOG_FATAL, "Elementary stream #%d is of unknown type, cannot proceed\n", i);
return AVERROR_INVALIDDATA;
} /* else { */
/* /\* if this stream must be remuxed *\/ */
/* ret = avcodec_copy_context(ofmt_ctx->streams[i]->codec, */
/* ifmt_ctx->streams[i]->codec); */
/* if (ret < 0) { */
/* av_log(NULL, AV_LOG_ERROR, "Copying stream context failed\n"); */
/* return ret; */
/* } */
/* } */
if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
enc_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
return 0;
}
static int open_output_file(const char *filename) {
int ret;
av_dump_format(ofmt_ctx, 0, filename, 1);
if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {
ret = avio_open(&ofmt_ctx->pb, filename, AVIO_FLAG_WRITE);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Could not open output file '%s'", filename);
return ret;
}
}
/* init muxer, write output file header */
ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error occurred when opening output file\n");
return ret;
}
return 0;
}
static int init_filter(FilteringContext* fctx, AVCodecContext *dec_ctx,
AVCodecContext *enc_ctx, const char *filter_spec) {
char args[512];
int ret = 0;
AVFilter *buffersrc = NULL;
AVFilter *buffersink = NULL;
AVFilterContext *buffersrc_ctx = NULL;
AVFilterContext *buffersink_ctx = NULL;
AVFilterInOut *outputs = avfilter_inout_alloc();
AVFilterInOut *inputs = avfilter_inout_alloc();
AVFilterGraph *filter_graph = avfilter_graph_alloc();
if (!outputs || !inputs || !filter_graph) {
ret = AVERROR(ENOMEM);
goto end;
}
if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
buffersrc = avfilter_get_by_name("buffer");
buffersink = avfilter_get_by_name("buffersink");
if (!buffersrc || !buffersink) {
av_log(NULL, AV_LOG_ERROR, "filtering source or sink element not found\n");
ret = AVERROR_UNKNOWN;
goto end;
}
snprintf(args, sizeof(args),
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt,
dec_ctx->time_base.num, dec_ctx->time_base.den,
dec_ctx->sample_aspect_ratio.num,
dec_ctx->sample_aspect_ratio.den);
ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",
args, NULL, filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n");
goto end;
}
ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",
NULL, NULL, filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n");
goto end;
}
ret = av_opt_set_bin(buffersink_ctx, "pix_fmts",
(uint8_t*)&enc_ctx->pix_fmt, sizeof(enc_ctx->pix_fmt),
AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n");
goto end;
}
} else if (dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
buffersrc = avfilter_get_by_name("abuffer");
buffersink = avfilter_get_by_name("abuffersink");
if (!buffersrc || !buffersink) {
av_log(NULL, AV_LOG_ERROR, "filtering source or sink element not found\n");
ret = AVERROR_UNKNOWN;
goto end;
}
if (!dec_ctx->channel_layout)
dec_ctx->channel_layout =
av_get_default_channel_layout(dec_ctx->channels);
snprintf(args, sizeof(args),
"time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%"PRIx64,
dec_ctx->time_base.num, dec_ctx->time_base.den, dec_ctx->sample_rate,
av_get_sample_fmt_name(dec_ctx->sample_fmt),
dec_ctx->channel_layout);
ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",
args, NULL, filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer source\n");
goto end;
}
ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",
NULL, NULL, filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer sink\n");
goto end;
}
ret = av_opt_set_bin(buffersink_ctx, "sample_fmts",
(uint8_t*)&enc_ctx->sample_fmt, sizeof(enc_ctx->sample_fmt),
AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot set output sample format\n");
goto end;
}
ret = av_opt_set_bin(buffersink_ctx, "channel_layouts",
(uint8_t*)&enc_ctx->channel_layout,
sizeof(enc_ctx->channel_layout), AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot set output channel layout\n");
goto end;
}
ret = av_opt_set_bin(buffersink_ctx, "sample_rates",
(uint8_t*)&enc_ctx->sample_rate, sizeof(enc_ctx->sample_rate),
AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot set output sample rate\n");
goto end;
}
} else {
ret = AVERROR_UNKNOWN;
goto end;
}
/* Endpoints for the filter graph. */
outputs->name = av_strdup("in");
outputs->filter_ctx = buffersrc_ctx;
outputs->pad_idx = 0;
outputs->next = NULL;
inputs->name = av_strdup("out");
inputs->filter_ctx = buffersink_ctx;
inputs->pad_idx = 0;
inputs->next = NULL;
if (!outputs->name || !inputs->name) {
ret = AVERROR(ENOMEM);
goto end;
}
if ((ret = avfilter_graph_parse_ptr(filter_graph, filter_spec,
&inputs, &outputs, NULL)) < 0)
goto end;
if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)
goto end;
/* Fill FilteringContext */
fctx->buffersrc_ctx = buffersrc_ctx;
fctx->buffersink_ctx = buffersink_ctx;
fctx->filter_graph = filter_graph;
end:
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs);
return ret;
}
static int init_filters(enum AVCodecID audioCodec) {
const char *filter_spec;
unsigned int i;
int ret;
filter_ctx = av_malloc_array(ifmt_ctx->nb_streams, sizeof(*filter_ctx));
if (!filter_ctx)
return AVERROR(ENOMEM);
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
filter_ctx[i].buffersrc_ctx = NULL;
filter_ctx[i].buffersink_ctx = NULL;
filter_ctx[i].filter_graph = NULL;
/* Skip streams that are neither audio nor video */
if (!(ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO
|| ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO))
continue;
if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
filter_spec = "null"; /* passthrough (dummy) filter for video */
else
/* TODO: make this more general */
if (audioCodec == AV_CODEC_ID_VORBIS) {
filter_spec = "asetnsamples=n=64";
} else {
/* filter_spec = "null"; /\* passthrough (dummy) filter for audio *\/ */
filter_spec = "fps=24";
/* filter_spec = "settb=expr=1/24"; */
}
ret = init_filter(&filter_ctx[i], ifmt_ctx->streams[i]->codec,
ofmt_ctx->streams[i]->codec, filter_spec);
if (ret)
return ret;
}
return 0;
}
static int encode_write_frame(AVFrame *filt_frame, unsigned int stream_index, int *got_frame) {
int ret;
int got_frame_local;
AVPacket enc_pkt;
int (*enc_func)(AVCodecContext *, AVPacket *, const AVFrame *, int *) =
(ifmt_ctx->streams[stream_index]->codec->codec_type ==
AVMEDIA_TYPE_VIDEO) ? avcodec_encode_video2 : avcodec_encode_audio2;
if (!got_frame)
got_frame = &got_frame_local;
/* av_log(NULL, AV_LOG_INFO, "Encoding frame\n"); */
/* encode filtered frame */
enc_pkt.data = NULL;
enc_pkt.size = 0;
av_init_packet(&enc_pkt);
ret = enc_func(ofmt_ctx->streams[stream_index]->codec, &enc_pkt,
filt_frame, got_frame);
av_frame_free(&filt_frame);
if (ret < 0)
return ret;
if (!(*got_frame))
return 0;
/* prepare packet for muxing */
enc_pkt.stream_index = stream_index;
enc_pkt.dts = av_rescale_q_rnd(enc_pkt.dts,
ofmt_ctx->streams[stream_index]->codec->time_base,
ofmt_ctx->streams[stream_index]->time_base,
AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
enc_pkt.pts = av_rescale_q_rnd(enc_pkt.pts,
ofmt_ctx->streams[stream_index]->codec->time_base,
ofmt_ctx->streams[stream_index]->time_base,
AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
enc_pkt.duration = av_rescale_q(enc_pkt.duration,
ofmt_ctx->streams[stream_index]->codec->time_base,
ofmt_ctx->streams[stream_index]->time_base);
/* av_log(NULL, AV_LOG_DEBUG, "Muxing frame\n"); */
/* mux encoded frame */
ret = av_interleaved_write_frame(ofmt_ctx, &enc_pkt);
return ret;
}
static int filter_encode_write_frame(AVFrame *frame, unsigned int stream_index) {
int ret;
AVFrame *filt_frame;
/* av_log(NULL, AV_LOG_INFO, "Pushing decoded frame to filters\n"); */
/* push the decoded frame into the filtergraph */
ret = av_buffersrc_add_frame_flags(filter_ctx[stream_index].buffersrc_ctx,
frame, 0);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n");
return ret;
}
/* pull filtered frames from the filtergraph */
while (1) {
filt_frame = av_frame_alloc();
if (!filt_frame) {
ret = AVERROR(ENOMEM);
break;
}
/* av_log(NULL, AV_LOG_INFO, "Pulling filtered frame from filters\n"); */
ret = av_buffersink_get_frame(filter_ctx[stream_index].buffersink_ctx,
filt_frame);
if (ret < 0) {
/* if no more frames for output - returns AVERROR(EAGAIN)
* if flushed and no more frames for output - returns AVERROR_EOF
* rewrite retcode to 0 to show it as normal procedure completion
*/
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
ret = 0;
av_frame_free(&filt_frame);
break;
}
filt_frame->pict_type = AV_PICTURE_TYPE_NONE;
ret = encode_write_frame(filt_frame, stream_index, NULL);
if (ret < 0)
break;
}
return ret;
}
static int flush_encoder(unsigned int stream_index) {
int ret;
int got_frame;
if (!(ofmt_ctx->streams[stream_index]->codec->codec->capabilities &
CODEC_CAP_DELAY))
return 0;
while (1) {
av_log(NULL, AV_LOG_INFO, "Flushing stream #%u encoder\n", stream_index);
ret = encode_write_frame(NULL, stream_index, &got_frame);
if (ret < 0)
break;
if (!got_frame)
return 0;
}
return ret;
}
static int transcode() {
int ret;
AVPacket packet = { .data = NULL, .size = 0 };
AVFrame *frame = NULL;
enum AVMediaType type;
unsigned int stream_index;
unsigned int i;
int got_frame;
int (*dec_func)(AVCodecContext *, AVFrame *, int *, const AVPacket *);
/* read all packets */
while (1) {
if ((ret = av_read_frame(ifmt_ctx, &packet)) < 0)
break;
stream_index = packet.stream_index;
type = ifmt_ctx->streams[packet.stream_index]->codec->codec_type;
av_log(NULL, AV_LOG_DEBUG, "Demuxer gave frame of stream_index %u\n",
stream_index);
if (filter_ctx[stream_index].filter_graph) {
av_log(NULL, AV_LOG_DEBUG, "Going to reencode&filter the frame\n");
frame = av_frame_alloc();
if (!frame) {
ret = AVERROR(ENOMEM);
break;
}
packet.dts = av_rescale_q_rnd(packet.dts,
ifmt_ctx->streams[stream_index]->time_base,
ifmt_ctx->streams[stream_index]->codec->time_base,
AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
packet.pts = av_rescale_q_rnd(packet.pts,
ifmt_ctx->streams[stream_index]->time_base,
ifmt_ctx->streams[stream_index]->codec->time_base,
AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
dec_func = (type == AVMEDIA_TYPE_VIDEO) ? avcodec_decode_video2 :
avcodec_decode_audio4;
ret = dec_func(ifmt_ctx->streams[stream_index]->codec, frame,
&got_frame, &packet);
if (ret < 0) {
av_frame_free(&frame);
av_log(NULL, AV_LOG_ERROR, "Decoding failed\n");
break;
}
if (got_frame) {
frame->pts = av_frame_get_best_effort_timestamp(frame);
ret = filter_encode_write_frame(frame, stream_index);
av_frame_free(&frame);
if (ret < 0)
goto end;
} else {
av_frame_free(&frame);
}
} else {
/* remux this frame without reencoding */
packet.dts = av_rescale_q_rnd(packet.dts,
ifmt_ctx->streams[stream_index]->time_base,
ofmt_ctx->streams[stream_index]->time_base,
AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
packet.pts = av_rescale_q_rnd(packet.pts,
ifmt_ctx->streams[stream_index]->time_base,
ofmt_ctx->streams[stream_index]->time_base,
AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
ret = av_interleaved_write_frame(ofmt_ctx, &packet);
if (ret < 0)
goto end;
}
av_free_packet(&packet);
}
/* flush filters and encoders */
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
/* flush filter */
if (!filter_ctx[i].filter_graph)
continue;
ret = filter_encode_write_frame(NULL, i);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Flushing filter failed\n");
goto end;
}
/* flush encoder */
ret = flush_encoder(i);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Flushing encoder failed\n");
goto end;
}
}
av_write_trailer(ofmt_ctx);
// Retrieve and store the first instance of codec statistics
// TODO: less naive, deal with multiple instances of statistics
for (i = 0; i < ofmt_ctx->nb_streams; i++) {
AVCodecContext* codec = ofmt_ctx->streams[i]->codec;
if ((codec->flags & CODEC_FLAG_PASS1) && (codec->stats_out)){
FILE* logfile = fopen(STATS_LOG, "wb");
fprintf(logfile, "%s", codec->stats_out);
if (fclose(logfile) < 0) {
av_log(NULL, AV_LOG_ERROR, "Error closing log file.\n");
}
break;
}
}
av_log(NULL, AV_LOG_INFO, "output duration = %" PRId64 "\n", ofmt_ctx->duration);
end:
av_free_packet(&packet);
av_frame_free(&frame);
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
avcodec_close(ifmt_ctx->streams[i]->codec);
if (ofmt_ctx && ofmt_ctx->nb_streams > i && ofmt_ctx->streams[i] && ofmt_ctx->streams[i]->codec)
avcodec_close(ofmt_ctx->streams[i]->codec);
if (filter_ctx && filter_ctx[i].filter_graph)
avfilter_graph_free(&filter_ctx[i].filter_graph);
}
av_free(filter_ctx);
avformat_close_input(&ifmt_ctx);
if (ofmt_ctx && !(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
avio_close(ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
if (ret < 0)
av_log(NULL, AV_LOG_ERROR, "Error occurred: %s\n", av_err2str(ret));
return ret ? 1 : 0;
}
int TranscodeToWebM(char* inputPath, char* outputPath, int audioBitRate, int crf, int videoMaxBitRate, int threads,
char* quality, int speed) {
int ret;
unsigned int pass;
char* stats = NULL;
av_register_all();
avfilter_register_all();
for (pass = 1; pass <= 2; pass++) {
if ((ret = open_input_file(inputPath)) < 0)
goto end;
if ((ret = init_output_context(outputPath)) < 0)
goto end;
if (pass == 2) {
size_t stats_length;
if (cmdutils_read_file(STATS_LOG, &stats, &stats_length) < 0) {
av_log(NULL, AV_LOG_ERROR, "Error reading stats file.\n");
break;
}
}
if ((ret = init_webm_encoders(audioBitRate, crf, videoMaxBitRate, threads, quality, speed, pass, stats)) < 0)
goto end;
if ((ret = open_output_file(outputPath)) < 0)
goto end;
if ((ret = init_filters(AV_CODEC_ID_VORBIS)) < 0)
goto end;
if ((ret = transcode()) < 0)
goto end;
}
if (remove(STATS_LOG) != 0) {
av_log(NULL, AV_LOG_ERROR, "Failed to remove %s\n", STATS_LOG);
}
end:
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error occurred: %s\n", av_err2str(ret));
return ret;
}
return 0;
}Here is the output from the ffmpeg command I am trying to mimic.
ffmpeg version N-62301-g59a5384 Copyright (c) 2000-2014 the FFmpeg developers
built on Apr 9 2014 09:58:44 with gcc 4.8.2 (GCC) 20140206 (prerelease)
configuration: --prefix=/opt/ffmpeg --extra-cflags=-I/opt/x264/include --extra-ldflags=-L/opt/x264/lib --extra-libs=-ldl --enable-gpl --enable-nonfree --enable-libfdk-aac --enable-libopus --enable-libvorbis --enable-libvpx --enable-libx264
libavutil 52. 75.100 / 52. 75.100
libavcodec 55. 58.103 / 55. 58.103
libavformat 55. 36.102 / 55. 36.102
libavdevice 55. 11.100 / 55. 11.100
libavfilter 4. 3.100 / 4. 3.100
libswscale 2. 6.100 / 2. 6.100
libswresample 0. 18.100 / 0. 18.100
libpostproc 52. 3.100 / 52. 3.100
Input #0, matroska,webm, from '/mnt/scratch/test_source/Sintel.2010.720p.mkv':
Metadata:
encoder : libebml v1.0.0 + libmatroska v1.0.0
creation_time : 2011-04-24 17:20:33
Duration: 00:14:48.03, start: 0.000000, bitrate: 6071 kb/s
Chapter #0.0: start 0.000000, end 103.125000
Metadata:
title : Chapter 01
Chapter #0.1: start 103.125000, end 148.667000
Metadata:
title : Chapter 02
Chapter #0.2: start 148.667000, end 349.792000
Metadata:
title : Chapter 03
Chapter #0.3: start 349.792000, end 437.208000
Metadata:
title : Chapter 04
Chapter #0.4: start 437.208000, end 472.075000
Metadata:
title : Chapter 05
Chapter #0.5: start 472.075000, end 678.833000
Metadata:
title : Chapter 06
Chapter #0.6: start 678.833000, end 744.083000
Metadata:
title : Chapter 07
Chapter #0.7: start 744.083000, end 888.032000
Metadata:
title : Chapter 08
Stream #0:0(eng): Video: h264 (High), yuv420p(tv, bt709), 1280x544, SAR 1:1 DAR 40:17, 24 fps, 24 tbr, 1k tbn, 48 tbc
Stream #0:1(eng): Audio: ac3, 48000 Hz, 5.1(side), fltp, 640 kb/s
Metadata:
title : AC3 5.1 @ 640 Kbps
Stream #0:2(ger): Subtitle: subrip
Stream #0:3(eng): Subtitle: subrip
Stream #0:4(spa): Subtitle: subrip
Stream #0:5(fre): Subtitle: subrip
Stream #0:6(ita): Subtitle: subrip
Stream #0:7(dut): Subtitle: subrip
Stream #0:8(pol): Subtitle: subrip
Stream #0:9(por): Subtitle: subrip
Stream #0:10(rus): Subtitle: subrip
Stream #0:11(vie): Subtitle: subrip
[libvpx @ 0x24b74c0] v1.3.0
Output #0, webm, to '/mnt/scratch/test_out/Sintel.2010.720p.script.webm':
Metadata:
encoder : Lavf55.36.102
Chapter #0.0: start 0.000000, end 103.125000
Metadata:
title : Chapter 01
Chapter #0.1: start 103.125000, end 148.667000
Metadata:
title : Chapter 02
Chapter #0.2: start 148.667000, end 349.792000
Metadata:
title : Chapter 03
Chapter #0.3: start 349.792000, end 437.208000
Metadata:
title : Chapter 04
Chapter #0.4: start 437.208000, end 472.075000
Metadata:
title : Chapter 05
Chapter #0.5: start 472.075000, end 678.833000
Metadata:
title : Chapter 06
Chapter #0.6: start 678.833000, end 744.083000
Metadata:
title : Chapter 07
Chapter #0.7: start 744.083000, end 888.032000
Metadata:
title : Chapter 08
Stream #0:0(eng): Video: vp8 (libvpx), yuv420p, 1280x544 [SAR 1:1 DAR 40:17], q=-1--1, pass 2, 60000 kb/s, 1k tbn, 24 tbc
Stream #0:1(eng): Audio: vorbis (libvorbis), 48000 Hz, 5.1(side), fltp, 384 kb/s
Metadata:
title : AC3 5.1 @ 640 Kbps
Stream mapping:
Stream #0:0 -> #0:0 (h264 -> libvpx)
Stream #0:1 -> #0:1 (ac3 -> libvorbis)
Press [q] to stop, [?] for help
frame=21312 fps= 11 q=0.0 Lsize= 567191kB time=00:14:48.01 bitrate=5232.4kbits/s
video:537377kB audio:29266kB subtitle:0kB other streams:0kB global headers:7kB muxing overhead: 0.096885% -
How to set CONFIG_PATH in Heroku buildpack
3 avril 2015, par johnklawlorBefore I elaborate on the problem, I should let you know that I’ve successfully installed ffmpegthumbnailer on heroku’s bash (heroku run bash).
I’m trying to build ffmpegthumbnailer using a custom Heroku buildpack I’ve written, but when I run ./configure && make install in the bin/compile buildpack file, ffmpegthumbnailer tells me that it can’t find the ffmpeg libs (util, format, codec, scale) and suggest that I set the PKG_CONFIG_PATH. So I set the PKG_CONFIG_PATH and it still can’t seem to find the libraries.
I’ve tried setting the PKG_CONFIG_PATH every which way. echo export, export, directly set it via PKG_CONFIG_PATH=/app... I’ve always set it inline with the .configure command. I even set PKG_CONFIG_PATH as a heroku config var, and tried to acces it via the ENV_DIR variable that’s passed into the buildpack as the third parameter, but I soon realized that I was simply setting a variable PKG_CONFIG_PATH to the contents of the file called PKG_CONFIG_PATH, i.e. I was setting PKG_CONFIG_PATH=/app/vendor..., which I’ve already done !! None of these have worked. Also, as you can see in the compile file and the log, echo $PKG_CONFIG_PATH prints the correct path.
What more debugging can i do ? What is going on ?
Below is my bin/compile buildpack file, and the log after I deploy my app to heroku.
#!/bin/sh
indent() {
sed -u 's/^/ /'
}
echo "-----> Install ffmpeg-thumbnailer"
BUILD_DIR=$1
VENDOR_DIR="vendor"
DOWNLOAD_URL="https://ffmpegthumbnailer.googlecode.com/files/ffmpegthumbnailer-2.0.8.tar.gz"
echo "DOWNLOAD_URL = " $DOWNLOAD_URL | indent
cd $BUILD_DIR
mkdir -p $VENDOR_DIR
cd $VENDOR_DIR
curl -L --silent $DOWNLOAD_URL | tar xz
cd ffmpegthumbnailer-2.0.8/
export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/app/vendor/ffmpeg/lib/pkgconfig/"
echo $PKG_CONFIG_PATH
./configure --prefix /app/vendor/ffmpegthumbnailer && make install
echo "exporting PATH and LIBRARY_PATH" | indent
PROFILE_PATH="$BUILD_DIR/.profile.d/ffmpeg-thumbnailer.sh"
mkdir -p $(dirname $PROFILE_PATH)
echo 'export PATH="$PATH:vendor/ffmpeg-thumbnailer/bin"' >> $PROFILE_PATH
echo 'export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:vendor/ffmpeg-thumbnailer/lib"' >> $PROFILE_PATHAnd my heroku deployment log...
Fetching repository, done.
Counting objects: 5, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 311 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
-----> Fetching custom git buildpack... done
-----> Multipack app detected
=====> Downloading Buildpack: https://github.com/shunjikonishi/heroku-buildpack-ffmpeg
=====> Detected Framework: ffmpeg
-----> Install ffmpeg
DOWNLOAD_URL = http://flect.github.io/heroku-binaries/libs/ffmpeg.tar.gz
exporting PATH and LIBRARY_PATH
=====> Downloading Buildpack: https://github.com/johnklawlor/heroku-buildpack-ffmpeg-thumbnailer
=====> Detected Framework: ffmpeg
-----> Install ffmpeg-thumbnailer
HERE COMES PKG_CONFIG!
/app/vendor/ffmpeg/lib/pkgconfig
DOWNLOAD_URL = https://ffmpegthumbnailer.googlecode.com/files/ffmpegthumbnailer-2.0.8.tar.gz
/app/vendor/ffmpeg/lib/pkgconfig
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... no
checking for mawk... mawk
checking whether make sets $(MAKE)... yes
checking for g++... g++
checking whether the C++ compiler works... yes
checking for C++ compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C++ compiler... yes
checking whether g++ accepts -g... yes
checking for style of include used by make... GNU
checking dependency style of g++... gcc3
checking build system type... x86_64-unknown-linux-gnu
checking host system type... x86_64-unknown-linux-gnu
checking how to print strings... printf
checking for gcc... gcc
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking dependency style of gcc... gcc3
checking for a sed that does not truncate output... /bin/sed
checking for grep that handles long lines and -e... /bin/grep
checking for egrep... /bin/grep -E
checking for fgrep... /bin/grep -F
checking for ld used by gcc... /usr/bin/ld
checking if the linker (/usr/bin/ld) is GNU ld... yes
checking for BSD- or MS-compatible name lister (nm)... /usr/bin/nm -B
checking the name lister (/usr/bin/nm -B) interface... BSD nm
checking whether ln -s works... yes
checking the maximum length of command line arguments... 1572864
checking whether the shell understands some XSI constructs... yes
checking whether the shell understands "+="... yes
checking how to convert x86_64-unknown-linux-gnu file names to x86_64-unknown-linux-gnu format... func_convert_file_noop
checking how to convert x86_64-unknown-linux-gnu file names to toolchain format... func_convert_file_noop
checking for /usr/bin/ld option to reload object files... -r
checking for objdump... objdump
checking how to recognize dependent libraries... pass_all
checking for dlltool... no
checking how to associate runtime and link libraries... printf %s\n
checking for ar... ar
checking for archiver @FILE support... @
checking for strip... strip
checking for ranlib... ranlib
checking command to parse /usr/bin/nm -B output from gcc object... ok
checking for sysroot... no
checking for mt... mt
checking if mt is a manifest tool... no
checking how to run the C preprocessor... gcc -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking for dlfcn.h... yes
checking for objdir... .libs
checking if gcc supports -fno-rtti -fno-exceptions... no
checking for gcc option to produce PIC... -fPIC -DPIC
checking if gcc PIC flag -fPIC -DPIC works... yes
checking if gcc static flag -static works... yes
checking if gcc supports -c -o file.o... yes
checking if gcc supports -c -o file.o... (cached) yes
checking whether the gcc linker (/usr/bin/ld -m elf_x86_64) supports shared libraries... yes
checking whether -lc should be explicitly linked in... no
checking dynamic linker characteristics... GNU/Linux ld.so
checking how to hardcode library paths into programs... immediate
checking whether stripping libraries is possible... yes
checking if libtool supports shared libraries... yes
checking whether to build shared libraries... yes
checking whether to build static libraries... yes
checking how to run the C++ preprocessor... g++ -E
checking for ld used by g++... /usr/bin/ld -m elf_x86_64
checking if the linker (/usr/bin/ld -m elf_x86_64) is GNU ld... yes
checking whether the g++ linker (/usr/bin/ld -m elf_x86_64) supports shared libraries... yes
checking for g++ option to produce PIC... -fPIC -DPIC
checking if g++ PIC flag -fPIC -DPIC works... yes
checking if g++ static flag -static works... yes
checking if g++ supports -c -o file.o... yes
checking if g++ supports -c -o file.o... (cached) yes
checking whether the g++ linker (/usr/bin/ld -m elf_x86_64) supports shared libraries... yes
checking dynamic linker characteristics... (cached) GNU/Linux ld.so
checking how to hardcode library paths into programs... immediate
checking for special C compiler options needed for large files... no
checking for _FILE_OFFSET_BITS value needed for large files... no
checking whether make supports nested variables... yes
checking for ANSI C header files... (cached) yes
checking for inttypes.h... (cached) yes
checking for sys/stat.h... (cached) yes
checking whether /usr/bin/ld -m elf_x86_64 accepts --as-needed... yes
checking for pkg-config... /usr/bin/pkg-config
checking pkg-config is at least version 0.9.0... yes
checking for FFMPEG... no
configure: error: Package requirements (libavutil libavformat libavcodec >= 52.26.0 libswscale) were not met:
No package 'libavutil' found
No package 'libavformat' found
No package 'libavcodec' found
No package 'libswscale' found
Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.
Alternatively, you may set the environment variables FFMPEG_CFLAGS
and FFMPEG_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.
exporting PATH and LIBRARY_PATH
=====> Downloading Buildpack: https://github.com/heroku/heroku-buildpack-ruby