#include <unistd.h>
#include <string.h>
#include <stdarg.h>
+#include <fcntl.h>
#include <limits.h>
// work arounds (centos)
#include <lzma.h>
#include "fileffmpeg.h"
#include "file.h"
#include "ffmpeg.h"
+#include "mainerror.h"
#include "mwindow.h"
#include "vframe.h"
Mutex FFMPEG::fflock("FFMPEG::fflock");
-static void ff_err(int ret, const char *msg)
+static void ff_err(int ret, const char *fmt, ...)
{
- char errmsg[BCSTRLEN]; av_strerror(ret, errmsg, sizeof(errmsg));
- fprintf(stderr,"%s: %s\n",msg, errmsg);
+ char msg[BCTEXTLEN];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(msg, sizeof(msg), fmt, ap);
+ va_end(ap);
+ char errmsg[BCSTRLEN];
+ av_strerror(ret, errmsg, sizeof(errmsg));
+ fprintf(stderr,_("%s err: %s\n"),msg, errmsg);
}
FFPacket::FFPacket()
fst->dequeue(this);
}
-FFAudioHistory::FFAudioHistory()
+int FFAudioStream::read(float *fp, long len)
{
- this->nch = 1;
- this->sz = 0x1000;
- bsz = sz * nch;
- bfr = new float[bsz];
- inp = outp = bfr;
- lmt = bfr + bsz;
+ long n = len * nch;
+ float *op = outp;
+ while( n > 0 ) {
+ int k = lmt - op;
+ if( k > n ) k = n;
+ n -= k;
+ while( --k >= 0 ) *fp++ = *op++;
+ if( op >= lmt ) op = bfr;
+ }
+ return len;
}
-FFAudioHistory::~FFAudioHistory()
+void FFAudioStream::realloc(long sz, int nch, long len)
{
- delete [] bfr;
+ long bsz = sz * nch;
+ float *np = new float[bsz];
+ inp = np + read(np, len) * nch;
+ outp = np;
+ lmt = np + bsz;
+ this->nch = nch;
+ this->sz = sz;
+ delete [] bfr; bfr = np;
}
-
-void FFAudioHistory::reserve(long sz, int nch)
+void FFAudioStream::realloc(long sz, int nch)
{
- long isz = inp - outp;
- sz += isz / nch;
- if( this->sz < sz || this->nch != nch ) {
- realloc(sz, nch);
- return;
+ if( sz > this->sz || this->nch != nch ) {
+ long len = this->nch != nch ? 0 : curr_pos - seek_pos;
+ if( len > this->sz ) len = this->sz;
+ iseek(len);
+ realloc(sz, nch, len);
}
- if( isz > 0 )
- memmove(bfr, outp, isz*sizeof(*bfr));
- outp = bfr;
- inp = bfr + isz;
}
-void FFAudioHistory::realloc(long sz, int nch)
+void FFAudioStream::reserve(long sz, int nch)
{
- if( this->sz >= sz && this->nch == nch ) return;
- long isz = used() * nch;
- this->nch = nch;
- this->sz = sz;
- bsz = sz * nch;
- float *np = new float[bsz];
- if( isz > 0 ) copy(np, isz);
- inp = np + isz;
- outp = np;
- lmt = np + bsz;
- delete [] bfr; bfr = np;
+ long len = (inp - outp) / nch;
+ sz += len;
+ if( sz > this->sz || this->nch != nch ) {
+ if( this->nch != nch ) len = 0;
+ realloc(sz, nch, len);
+ return;
+ }
+ if( (len*=nch) > 0 && bfr != outp )
+ memmove(bfr, outp, len*sizeof(*bfr));
+ outp = bfr;
+ inp = bfr + len;
}
-long FFAudioHistory::used()
+long FFAudioStream::used()
{
long len = inp>=outp ? inp-outp : inp-bfr + lmt-outp;
return len / nch;
}
-long FFAudioHistory::avail()
+long FFAudioStream::avail()
{
float *in1 = inp+1;
if( in1 >= lmt ) in1 = bfr;
long len = outp >= in1 ? outp-in1 : outp-bfr + lmt-in1;
return len / nch;
}
-void FFAudioHistory::reset() // clear bfr
+void FFAudioStream::reset() // clear bfr
{
inp = outp = bfr;
}
-void FFAudioHistory::iseek(int64_t ofs)
+void FFAudioStream::iseek(int64_t ofs)
{
outp = inp - ofs*nch;
- if( outp < bfr ) outp += bsz;
+ if( outp < bfr ) outp += sz*nch;
}
-float *FFAudioHistory::get_outp(int ofs)
+float *FFAudioStream::get_outp(int ofs)
{
float *ret = outp;
outp += ofs*nch;
return ret;
}
-int64_t FFAudioHistory::get_inp(int ofs)
+int64_t FFAudioStream::put_inp(int ofs)
{
inp += ofs*nch;
- return (inp-bfr) / nch;
+ return (inp-outp) / nch;
}
-int FFAudioHistory::write(const float *fp, long len)
+int FFAudioStream::write(const float *fp, long len)
{
long n = len * nch;
+ float *ip = inp;
while( n > 0 ) {
- int k = lmt - inp;
+ int k = lmt - ip;
if( k > n ) k = n;
n -= k;
- while( --k >= 0 ) *inp++ = *fp++;
- if( inp >= lmt ) inp -= bsz;
+ while( --k >= 0 ) *ip++ = *fp++;
+ if( ip >= lmt ) ip = bfr;
}
+ inp = ip;
return len;
}
-int FFAudioHistory::copy(float *fp, long len)
-{
- long n = len;
- while( n > 0 ) {
- int k = lmt - outp;
- if( k > n ) k = n;
- n -= k;
- while( --k >= 0 ) *fp++ = *outp++;
- if( outp >= lmt ) outp -= bsz;
- }
- return len;
-}
-
-int FFAudioHistory::zero(long len)
+int FFAudioStream::zero(long len)
{
long n = len * nch;
+ float *ip = inp;
while( n > 0 ) {
- int k = lmt - inp;
+ int k = lmt - ip;
if( k > n ) k = n;
n -= k;
- while( --k >= 0 ) *inp++ = 0;
- if( inp >= lmt ) inp -= bsz;
+ while( --k >= 0 ) *ip++ = 0;
+ if( ip >= lmt ) ip = bfr;
}
+ inp = ip;
return len;
}
-int FFAudioHistory::read(double *dp, long len, int ch)
+// does not advance outp
+int FFAudioStream::read(double *dp, long len, int ch)
{
- long sz = used();
- if( !sz ) return 0;
- if( len > sz ) len = sz;
long n = len;
float *op = outp + ch;
+ float *lmt1 = lmt + nch-1;
while( n > 0 ) {
- int k = (lmt - outp) / nch;
+ int k = (lmt1 - op) / nch;
if( k > n ) k = n;
n -= k;
while( --k >= 0 ) { *dp++ = *op; op += nch; }
- if( op >= lmt ) op -= bsz;
+ if( op >= lmt ) op -= sz*nch;
}
return len;
}
// load linear buffer, no wrapping allowed, does not advance inp
-int FFAudioHistory::write(const double *dp, long len, int ch)
+int FFAudioStream::write(const double *dp, long len, int ch)
{
- long osz = avail();
- if( !osz || !len ) return 0;
- if( len > osz ) len = osz;
long n = len;
float *ip = inp + ch;
while( --n >= 0 ) { *ip = *dp++; ip += nch; }
if( ret >= 0 )
reading = 1;
else
- fprintf(stderr, "FFStream::decode_activate: open decoder failed\n");
+ eprintf("FFStream::decode_activate: open decoder failed\n");
}
else
- fprintf(stderr, "FFStream::decode_activate: can't clone input file\n");
+ eprintf("FFStream::decode_activate: can't clone input file\n");
av_dict_free(&copts);
ff_unlock();
}
if( ret >= 0 ) return 1;
st_eof(1);
if( ret == AVERROR_EOF ) return 0;
- fprintf(stderr, "FFStream::read_packet: av_read_frame failed\n");
+ ff_err(ret, "FFStream::read_packet: av_read_frame failed\n");
flushed = 1;
return -1;
}
if( !ret ) ipkt->stream_index = st->index;
}
if( ipkt->stream_index == st->index ) {
- while( ipkt->size > 0 && !got_frame ) {
+ while( (ipkt->size > 0 || !ipkt->data) && !got_frame ) {
ret = decode_frame(frame, got_frame);
- if( ret < 0 ) break;
+ if( ret < 0 || !ipkt->data ) break;
ipkt->data += ret;
ipkt->size -= ret;
}
frame, AV_BUFFERSRC_FLAG_KEEP_REF);
if( ret < 0 ) {
av_frame_unref(frame);
- fprintf(stderr, "FFStream::load_filter: av_buffersrc_add_frame_flags failed\n");
+ eprintf("FFStream::load_filter: av_buffersrc_add_frame_flags failed\n");
}
return ret;
}
if( ret < 0 ) {
if( ret == AVERROR(EAGAIN) ) return 0;
if( ret == AVERROR_EOF ) { st_eof(1); return -1; }
- fprintf(stderr, "FFStream::read_filter: av_buffersink_get_frame failed\n");
+ ff_err(ret, "FFStream::read_filter: av_buffersink_get_frame failed\n");
return ret;
}
return 1;
return ret;
}
+int FFStream::write_packet(FFPacket &pkt)
+{
+ bs_filter(pkt);
+ av_packet_rescale_ts(pkt, st->codec->time_base, st->time_base);
+ pkt->stream_index = st->index;
+ return av_interleaved_write_frame(ffmpeg->fmt_ctx, pkt);
+}
+
+int FFStream::flush()
+{
+ int ret = 0;
+ while( ret >= 0 ) {
+ FFPacket pkt;
+ int got_packet = 0;
+ ret = encode_frame(pkt, 0, got_packet);
+ if( ret < 0 || !got_packet ) break;
+ ret = write_packet(pkt);
+ }
+ if( ret < 0 )
+ ff_err(ret, "FFStream::flush");
+ return ret >= 0 ? 0 : 1;
+}
+
FFAudioStream::FFAudioStream(FFMPEG *ffmpeg, AVStream *strm, int idx)
: FFStream(ffmpeg, strm, idx)
{
aud_bfr_sz = 0;
aud_bfr = 0;
+
+// history buffer
+ nch = 2;
+ sz = 0x10000;
+ long bsz = sz * nch;
+ bfr = new float[bsz];
+ inp = outp = bfr;
+ lmt = bfr + bsz;
}
FFAudioStream::~FFAudioStream()
{
if( resample_context ) swr_free(&resample_context);
delete [] aud_bfr;
+ delete [] bfr;
}
-int FFAudioStream::load_history(float *&bfr, int len)
+int FFAudioStream::load_history(uint8_t **data, int len)
{
+ float *samples = *(float **)data;
if( resample_context ) {
if( len > aud_bfr_sz ) {
delete [] aud_bfr;
aud_bfr = new float[aud_bfr_sz*channels];
}
int ret = swr_convert(resample_context,
- (uint8_t**)&aud_bfr, aud_bfr_sz,
- (const uint8_t**)&bfr, len);
+ (uint8_t**)&aud_bfr, aud_bfr_sz, (const uint8_t**)data, len);
if( ret < 0 ) {
- fprintf(stderr, "FFAudioStream::load_history: swr_convert failed\n");
+ ff_err(ret, "FFAudioStream::load_history: swr_convert failed\n");
return -1;
}
- bfr = aud_bfr;
+ samples = aud_bfr;
len = ret;
}
- append_history(bfr, len);
+ // biggest user bfr since seek + frame
+ realloc(mbsz + len + 1, channels);
+ write(samples, len);
return len;
}
{
int ret = avcodec_decode_audio4(st->codec, frame, &got_frame, ipkt);
if( ret < 0 ) {
- fprintf(stderr, "FFAudioStream::decode_frame: Could not read audio frame\n");
+ ff_err(ret, "FFAudioStream::decode_frame: Could not read audio frame\n");
return -1;
}
return ret;
10000 : ctx->frame_size;
}
-void FFAudioStream::alloc_history(int len)
-{
- history.realloc(len+1, channels);
-}
-
-void FFAudioStream::reserve_history(int len)
-{
- history.reserve(len+1, st->codec->channels);
-}
-
-void FFAudioStream::append_history(const float *fp, int len)
-{
- // biggest user bfr since seek + length this frame
- int hsz = mbsz + len;
- alloc_history(hsz);
- history.write(fp, len);
-}
-
int64_t FFAudioStream::load_buffer(double ** const sp, int len)
{
- reserve_history(len);
- int nch = st->codec->channels;
+ reserve(len+1, st->codec->channels);
for( int ch=0; ch<nch; ++ch )
- history.write(sp[ch], len, ch);
- return history.get_inp(len);
-}
-
-void FFAudioStream::zero_history(int len)
-{
- history.zero(len);
-}
-
-float* FFAudioStream::get_history(int len)
-{
- return history.get_outp(len);
+ write(sp[ch], len, ch);
+ return put_inp(len);
}
int FFAudioStream::in_history(int64_t pos)
{
if( pos > curr_pos ) return 0;
int64_t len = curr_pos - seek_pos;
- if( len > history.sz ) len = history.sz;
+ if( len > sz ) len = sz;
if( pos < curr_pos - len ) return 0;
return 1;
}
frame->sample_rate = ctx->sample_rate;
int ret = av_frame_get_buffer(frame, 0);
if (ret < 0)
- fprintf(stderr, "FFAudioStream::init_frame: av_frame_get_buffer failed\n");
+ ff_err(ret, "FFAudioStream::init_frame: av_frame_get_buffer failed\n");
return ret;
}
for( int i=0; ret>=0 && !flushed && curr_pos<end_pos && i<1000; ++i ) {
ret = read_frame(frame);
if( ret > 0 ) {
- load_history((float *&)frame->extended_data[0], frame->nb_samples);
+ load_history(&frame->extended_data[0], frame->nb_samples);
curr_pos += frame->nb_samples;
}
}
- if( flushed && end_pos > curr_pos ) {
- zero_history(end_pos - curr_pos);
+ if( end_pos > curr_pos ) {
+ zero(end_pos - curr_pos);
curr_pos = end_pos;
}
return curr_pos - pos;
{
if( decode_activate() < 0 ) return -1;
if( in_history(pos) ) {
- history.iseek(curr_pos - pos);
+ iseek(curr_pos - pos);
return 0;
}
if( pos == curr_pos ) return 0;
+ if( !st->codec || !st->codec->codec ) return -1;
+ avcodec_flush_buffers(st->codec);
double secs = (double)pos / sample_rate;
int64_t tstmp = secs * st->time_base.den / st->time_base.num;
if( nudge != AV_NOPTS_VALUE ) tstmp += nudge;
avformat_seek_file(fmt_ctx, st->index, -INT64_MAX, tstmp, INT64_MAX, 0);
seek_pos = curr_pos = pos;
- mbsz = 0;
- history.reset();
- st_eof(0);
+ reset(); st_eof(0);
+ mbsz = 0; flushed = 0; need_packet = 1;
return 1;
}
frm = new FFrame(this);
if( (ret=frm->initted()) < 0 ) break;
AVFrame *frame = *frm;
- float *bfrp = get_history(frame_sz);
+ float *bfrp = get_outp(frame_sz);
ret = swr_convert(resample_context,
(uint8_t **)frame->extended_data, frame_sz,
(const uint8_t **)&bfrp, frame_sz);
if( ret < 0 ) {
- fprintf(stderr, "FFAudioStream::encode: swr_convert failed\n");
+ ff_err(ret, "FFAudioStream::encode: swr_convert failed\n");
break;
}
frm->queue(curr_pos);
return ret >= 0 ? 0 : 1;
}
+int FFAudioStream::encode_frame(FFPacket &pkt, AVFrame *frame, int &got_packet)
+{
+ int ret = avcodec_encode_audio2(st->codec, pkt, frame, &got_packet);
+ if( ret < 0 ) {
+ ff_err(ret, "FFAudioStream::encode_frame: encode audio failed\n");
+ return -1;
+ }
+ return ret;
+}
+
FFVideoStream::FFVideoStream(FFMPEG *ffmpeg, AVStream *strm, int idx)
: FFStream(ffmpeg, strm, idx)
{
{
int ret = avcodec_decode_video2(st->codec, frame, &got_frame, ipkt);
if( ret < 0 ) {
- fprintf(stderr, "FFVideoStream::decode_frame: Could not read video frame\n");
+ ff_err(ret, "FFVideoStream::decode_frame: Could not read video frame\n");
return -1;
}
if( got_frame )
int FFVideoStream::load(VFrame *vframe, int64_t pos)
{
- if( video_seek(pos) < 0 ) return -1;
+ int ret = video_seek(pos);
+ if( ret < 0 ) return -1;
if( !frame && !(frame=av_frame_alloc()) ) {
fprintf(stderr, "FFVideoStream::load: av_frame_alloc failed\n");
return -1;
}
- int ret = 0;
for( int i=0; ret>=0 && !flushed && curr_pos<=pos && i<1000; ++i ) {
ret = read_frame(frame);
}
if( gop < 4 ) gop = 4;
if( gop > 64 ) gop = 64;
if( pos >= curr_pos && pos <= curr_pos + gop ) return 0;
+ if( pos == curr_pos-1 && curr_pos > seek_pos ) return 1;
+ if( !st->codec || !st->codec->codec ) return -1;
+ avcodec_flush_buffers(st->codec);
// back up a few frames to read up to current to help repair damages
if( (pos-=gop) < 0 ) pos = 0;
double secs = (double)pos / frame_rate;
avformat_seek_file(fmt_ctx, st->index, -INT64_MAX, tstmp, INT64_MAX, 0);
seek_pos = curr_pos = pos;
st_eof(0);
+ flushed = 0; need_packet = 1;
return 1;
}
return ret >= 0 ? 0 : 1;
}
+int FFVideoStream::encode_frame(FFPacket &pkt, AVFrame *frame, int &got_packet)
+{
+ int ret = avcodec_encode_video2(st->codec, pkt, frame, &got_packet);
+ if( ret < 0 ) {
+ ff_err(ret, "FFVideoStream::encode_frame: encode video failed\n");
+ return -1;
+ }
+ return ret;
+}
PixelFormat FFVideoStream::color_model_to_pix_fmt(int color_model)
{
" sws_getCachedContext() failed\n");
return 1;
}
- if( sws_scale(convert_ctx, ip->data, ip->linesize, 0, ih,
- opic.data, opic.linesize) < 0 ) {
- fprintf(stderr, "FFVideoStream::convert_picture_frame: sws_scale() failed\n");
+ int ret = sws_scale(convert_ctx, ip->data, ip->linesize, 0, ih,
+ opic.data, opic.linesize);
+ if( ret < 0 ) {
+ ff_err(ret, "FFVideoStream::convert_picture_frame: sws_scale() failed\n");
return 1;
}
return 0;
" sws_getCachedContext() failed\n");
return 1;
}
- if( sws_scale(convert_ctx, opic.data, opic.linesize, 0, frame->get_h(),
- op->data, op->linesize) < 0 ) {
- fprintf(stderr, "FFVideoStream::convert_frame_picture: sws_scale() failed\n");
+ int ret = sws_scale(convert_ctx, opic.data, opic.linesize, 0, frame->get_h(),
+ op->data, op->linesize);
+ if( ret < 0 ) {
+ ff_err(ret, "FFVideoStream::convert_frame_picture: sws_scale() failed\n");
return 1;
}
return 0;
AVRational FFMPEG::to_sample_aspect_ratio(double aspect_ratio)
{
+#if 1
int height = 1000000, width = height * aspect_ratio;
float w, h;
MWindow::create_aspect_ratio(w, h, width, height);
return (AVRational){(int)w, (int)h};
+#else
+// square pixels
+ return (AVRational){1, 1};
+#endif
}
AVRational FFMPEG::to_time_base(int sample_rate)
return (AVRational){1, sample_rate};
}
-extern void get_exe_path(char *result); // from main.C
-
void FFMPEG::set_option_path(char *path, const char *fmt, ...)
{
get_exe_path(path);
set_option_path(path, "%s/%s", type, spec);
}
-int FFMPEG::check_option(const char *path, char *spec)
+int FFMPEG::get_format(char *format, const char *path, char *spec)
{
- char option_path[BCTEXTLEN], line[BCTEXTLEN];
- char format[BCSTRLEN], codec[BCSTRLEN];
+ char option_path[BCTEXTLEN], line[BCTEXTLEN], codec[BCTEXTLEN];
get_option_path(option_path, path, spec);
FILE *fp = fopen(option_path,"r");
if( !fp ) return 1;
line[sizeof(line)-1] = 0;
ret = scan_option_line(line, format, codec);
}
- if( !ret ) {
- if( !file_format[0] ) strcpy(file_format, format);
- else if( strcmp(file_format, format) ) ret = 1;
- }
fclose(fp);
return ret;
}
-const char *FFMPEG::get_file_format()
+int FFMPEG::get_file_format()
{
- file_format[0] = 0;
int ret = 0;
+ char audio_format[BCSTRLEN], video_format[BCSTRLEN];
+ file_format[0] = audio_format[0] = video_format[0] = 0;
Asset *asset = file_base->asset;
if( !ret && asset->audio_data )
- ret = check_option("audio", asset->acodec);
+ ret = get_format(audio_format, "audio", asset->acodec);
if( !ret && asset->video_data )
- ret = check_option("video", asset->vcodec);
- if( !ret && !file_format[0] ) ret = 1;
- return !ret ? file_format : 0;
+ ret = get_format(video_format, "video", asset->vcodec);
+ if( !ret && !audio_format[0] && !video_format[0] )
+ ret = 1;
+ if( !ret && audio_format[0] && video_format[0] &&
+ strcmp(audio_format, video_format) ) ret = -1;
+ if( !ret )
+ strcpy(file_format, audio_format[0] ? audio_format : video_format);
+ return ret;
}
int FFMPEG::scan_option_line(char *cp, char *tag, char *val)
return 0;
}
-int FFMPEG::read_options(const char *options, char *format, char *codec,
- char *bsfilter, char *bsargs, AVDictionary *&opts)
+int FFMPEG::get_encoder(const char *options,
+ char *format, char *codec, char *bsfilter, char *bsargs)
{
FILE *fp = fopen(options,"r");
if( !fp ) {
- fprintf(stderr, "FFMPEG::read_options: options open failed %s\n",options);
+ eprintf("FFMPEG::get_encoder: options open failed %s\n",options);
return 1;
}
- int ret = read_options(fp, options, format, codec, opts);
- char *cp = codec;
- while( *cp && *cp != '|' ) ++cp;
- if( *cp == '|' && !scan_option_line(cp+1, bsfilter, bsargs) ) {
- do { *cp-- = 0; } while( cp>=codec && (*cp==' ' || *cp == '\t' ) );
- }
- else
- bsfilter[0] = bsargs[0] = 0;
+ if( get_encoder(fp, format, codec, bsfilter, bsargs) )
+ eprintf(_("FFMPEG::get_encoder:"
+ " err: format/codec not found %s\n"), options);
fclose(fp);
- return ret;
+ return 0;
}
-int FFMPEG::read_options(FILE *fp, const char *options,
- char *format, char *codec, AVDictionary *&opts)
+int FFMPEG::get_encoder(FILE *fp,
+ char *format, char *codec, char *bsfilter, char *bsargs)
{
+ format[0] = codec[0] = bsfilter[0] = bsargs[0] = 0;
char line[BCTEXTLEN];
- if( !fgets(line, sizeof(line), fp) ) {
- fprintf(stderr, "FFMPEG::read_options:"
- " options file empty %s\n",options);
- return 1;
- }
+ if( !fgets(line, sizeof(line), fp) ) return 1;
line[sizeof(line)-1] = 0;
- if( scan_option_line(line, format, codec) ) {
- fprintf(stderr, "FFMPEG::read_options:"
- " err: format/codec not found %s\n", options);
- return 1;
- }
- return read_options(fp, options, opts, 1);
+ if( scan_option_line(line, format, codec) ) return 1;
+ char *cp = codec;
+ while( *cp && *cp != '|' ) ++cp;
+ if( !*cp ) return 0;
+ if( scan_option_line(cp+1, bsfilter, bsargs) ) return 1;
+ do { *cp-- = 0; } while( cp>=codec && (*cp==' ' || *cp == '\t' ) );
+ return 0;
}
int FFMPEG::read_options(const char *options, AVDictionary *&opts)
return ret;
}
-int FFMPEG::read_options(FILE *fp, const char *options, AVDictionary *&opts, int no)
+int FFMPEG::scan_options(const char *options, AVDictionary *&opts, AVStream *st)
{
- int ret = 0;
+ FILE *fp = fmemopen((void *)options,strlen(options),"r");
+ if( !fp ) return 0;
+ int ret = read_options(fp, options, opts);
+ fclose(fp);
+ AVDictionaryEntry *tag = av_dict_get(opts, "id", NULL, 0);
+ if( tag ) st->id = strtol(tag->value,0,0);
+ return ret;
+}
+
+int FFMPEG::read_options(FILE *fp, const char *options, AVDictionary *&opts)
+{
+ int ret = 0, no = 0;
char line[BCTEXTLEN];
while( !ret && fgets(line, sizeof(line), fp) ) {
line[sizeof(line)-1] = 0;
++no;
if( line[0] == '#' ) continue;
+ if( line[0] == '\n' ) continue;
char key[BCSTRLEN], val[BCTEXTLEN];
if( scan_option_line(line, key, val) ) {
- fprintf(stderr, "FFMPEG::read_options:"
- " err reading %s: line %d\n", options, no);
+ eprintf(_("FFMPEG::read_options:"
+ " err reading %s: line %d\n"), options, no);
ret = 1;
}
if( !ret ) {
return read_options(option_path, opts);
}
+int FFMPEG::load_options(const char *path, char *bfr, int len)
+{
+ *bfr = 0;
+ FILE *fp = fopen(path, "r");
+ if( !fp ) return 1;
+ fgets(bfr, len, fp); // skip hdr
+ len = fread(bfr, 1, len-1, fp);
+ if( len < 0 ) len = 0;
+ bfr[len] = 0;
+ fclose(fp);
+ return 0;
+}
+
void FFMPEG::set_loglevel(const char *ap)
{
if( !ap || !*ap ) return;
for( int i=0; i<(int)fmt_ctx->nb_streams; ++i ) {
AVStream *st = fmt_ctx->streams[i];
AVCodecContext *avctx = st->codec;
- report("stream %d, id 0x%06x:\n", i, avctx->codec_id);
+ report(_("stream %d, id 0x%06x:\n"), i, avctx->codec_id);
const AVCodecDescriptor *desc = avcodec_descriptor_get(avctx->codec_id);
if( avctx->codec_type == AVMEDIA_TYPE_VIDEO ) {
AVRational framerate = av_guess_frame_rate(fmt_ctx, st, 0);
report(" %d:%02d:%05.2f\n", hrs, mins, secs);
}
else
- report(" codec_type unknown\n");
+ report(_(" codec_type unknown\n"));
}
report("\n");
for( int i=0; i<(int)fmt_ctx->nb_programs; ++i ) {
fp = fopen(file_opts, "r");
}
if( fp ) {
- read_options(fp, file_opts, opts, 0);
+ read_options(fp, file_opts, opts);
fclose(fp);
}
else
{
struct stat st;
if( stat(fmt_ctx->filename, &st) < 0 ) {
- fprintf(stderr,"FFMPEG::open_decoder: can't stat file: %s\n",
+ eprintf("FFMPEG::open_decoder: can't stat file: %s\n",
fmt_ctx->filename);
return 1;
}
int FFMPEG::init_encoder(const char *filename)
{
- const char *format = get_file_format();
- if( !format ) {
- fprintf(stderr, "FFMPEG::init_encoder: invalid file format for %s\n", filename);
+ int fd = ::open(filename,O_WRONLY);
+ if( fd < 0 ) fd = open(filename,O_WRONLY+O_CREAT,0666);
+ if( fd < 0 ) {
+ eprintf("FFMPEG::init_encoder: bad file path: %s\n", filename);
+ return 1;
+ }
+ ::close(fd);
+ int ret = get_file_format();
+ if( ret > 0 ) {
+ eprintf("FFMPEG::init_encoder: bad file format: %s\n", filename);
+ return 1;
+ }
+ if( ret < 0 ) {
+ eprintf("FFMPEG::init_encoder: mismatch audio/video file format: %s\n", filename);
return 1;
}
- int ret = 0;
ff_lock("FFMPEG::init_encoder");
av_register_all();
- avformat_alloc_output_context2(&fmt_ctx, 0, format, filename);
+ avformat_alloc_output_context2(&fmt_ctx, 0, file_format, filename);
if( !fmt_ctx ) {
- fprintf(stderr, "FFMPEG::init_encoder: failed: %s\n", filename);
+ eprintf("FFMPEG::init_encoder: failed: %s\n", filename);
ret = 1;
}
if( !ret ) {
return ret;
}
-int FFMPEG::open_encoder(const char *path, const char *spec)
+int FFMPEG::open_encoder(const char *type, const char *spec)
{
Asset *asset = file_base->asset;
AVDictionary *sopts = 0;
av_dict_copy(&sopts, opts, 0);
char option_path[BCTEXTLEN];
- set_option_path(option_path, "%s/%s.opts", path, path);
+ set_option_path(option_path, "%s/%s.opts", type, type);
read_options(option_path, sopts);
- get_option_path(option_path, path, spec);
+ get_option_path(option_path, type, spec);
char format_name[BCSTRLEN], codec_name[BCTEXTLEN];
char bsfilter[BCSTRLEN], bsargs[BCTEXTLEN];
- if( read_options(option_path, format_name, codec_name, bsfilter, bsargs, sopts) ) {
- fprintf(stderr, "FFMPEG::open_encoder: read options failed %s:%s\n",
+ if( get_encoder(option_path, format_name, codec_name, bsfilter, bsargs) ) {
+ eprintf("FFMPEG::open_encoder: get_encoder failed %s:%s\n",
option_path, filename);
return 1;
}
const AVCodecDescriptor *codec_desc = 0;
AVCodec *codec = avcodec_find_encoder_by_name(codec_name);
if( !codec ) {
- fprintf(stderr, "FFMPEG::open_encoder: cant find codec %s:%s\n",
+ eprintf("FFMPEG::open_encoder: cant find codec %s:%s\n",
codec_name, filename);
ret = 1;
}
if( !ret ) {
codec_desc = avcodec_descriptor_get(codec->id);
if( !codec_desc ) {
- fprintf(stderr, "FFMPEG::open_encoder: unknown codec %s:%s\n",
+ eprintf("FFMPEG::open_encoder: unknown codec %s:%s\n",
codec_name, filename);
ret = 1;
}
if( !ret ) {
st = avformat_new_stream(fmt_ctx, 0);
if( !st ) {
- fprintf(stderr, "FFMPEG::open_encoder: cant create stream %s:%s\n",
+ eprintf("FFMPEG::open_encoder: cant create stream %s:%s\n",
codec_name, filename);
ret = 1;
}
switch( codec_desc->type ) {
case AVMEDIA_TYPE_AUDIO: {
if( has_audio ) {
- fprintf(stderr, "FFMPEG::open_encoder: duplicate audio %s:%s\n",
+ eprintf("FFMPEG::open_encoder: duplicate audio %s:%s\n",
codec_name, filename);
ret = 1;
break;
}
has_audio = 1;
+ if( scan_options(asset->ff_audio_options, sopts, st) ) {
+ eprintf("FFMPEG::open_encoder: bad audio options %s:%s\n",
+ codec_name, filename);
+ ret = 1;
+ break;
+ }
+ if( asset->ff_audio_bitrate > 0 ) {
+ ctx->bit_rate = asset->ff_audio_bitrate;
+ char arg[BCSTRLEN];
+ sprintf(arg, "%d", asset->ff_audio_bitrate);
+ av_dict_set(&sopts, "b", arg, 0);
+ }
int aidx = ffaudio.size();
int idx = aidx + ffvideo.size();
FFAudioStream *aud = new FFAudioStream(this, st, idx);
ctx->channel_layout = av_get_default_channel_layout(ctx->channels);
ctx->sample_rate = check_sample_rate(codec, asset->sample_rate);
if( !ctx->sample_rate ) {
- fprintf(stderr, "FFMPEG::open_audio_encode:"
+ eprintf("FFMPEG::open_audio_encode:"
" check_sample_rate failed %s\n", filename);
ret = 1;
break;
break; }
case AVMEDIA_TYPE_VIDEO: {
if( has_video ) {
- fprintf(stderr, "FFMPEG::open_encoder: duplicate video %s:%s\n",
+ eprintf("FFMPEG::open_encoder: duplicate video %s:%s\n",
codec_name, filename);
ret = 1;
break;
}
has_video = 1;
+ if( scan_options(asset->ff_video_options, sopts, st) ) {
+ eprintf("FFMPEG::open_encoder: bad video options %s:%s\n",
+ codec_name, filename);
+ ret = 1;
+ break;
+ }
+ if( asset->ff_video_bitrate > 0 ) {
+ ctx->bit_rate = asset->ff_video_bitrate;
+ char arg[BCSTRLEN];
+ sprintf(arg, "%d", asset->ff_video_bitrate);
+ av_dict_set(&sopts, "b", arg, 0);
+ }
+ else if( asset->ff_video_quality > 0 ) {
+ ctx->global_quality = asset->ff_video_quality * FF_QP2LAMBDA;
+ ctx->qmin = ctx->qmax = asset->ff_video_quality;
+ ctx->mb_lmin = ctx->qmin * FF_QP2LAMBDA;
+ ctx->mb_lmax = ctx->qmax * FF_QP2LAMBDA;
+ ctx->flags |= CODEC_FLAG_QSCALE;
+ char arg[BCSTRLEN];
+ av_dict_set(&sopts, "flags", "+qscale", 0);
+ sprintf(arg, "%d", asset->ff_video_quality);
+ av_dict_set(&sopts, "qscale", arg, 0);
+ sprintf(arg, "%d", ctx->global_quality);
+ av_dict_set(&sopts, "global_quality", arg, 0);
+ }
int vidx = ffvideo.size();
int idx = vidx + ffaudio.size();
FFVideoStream *vid = new FFVideoStream(this, st, idx);
ctx->pix_fmt = codec->pix_fmts ? codec->pix_fmts[0] : AV_PIX_FMT_YUV420P;
AVRational frame_rate = check_frame_rate(codec, vid->frame_rate);
if( !frame_rate.num || !frame_rate.den ) {
- fprintf(stderr, "FFMPEG::open_audio_encode:"
+ eprintf("FFMPEG::open_audio_encode:"
" check_frame_rate failed %s\n", filename);
ret = 1;
break;
vid->writing = -1;
break; }
default:
- fprintf(stderr, "FFMPEG::open_encoder: not audio/video, %s:%s\n",
+ eprintf("FFMPEG::open_encoder: not audio/video, %s:%s\n",
codec_name, filename);
ret = 1;
}
ret = avcodec_open2(st->codec, codec, &sopts);
if( ret < 0 ) {
ff_err(ret,"FFMPEG::open_encoder");
- fprintf(stderr, "FFMPEG::open_encoder: open failed %s:%s\n",
+ eprintf("FFMPEG::open_encoder: open failed %s:%s\n",
codec_name, filename);
ret = 1;
}
int FFMPEG::encode_activate()
{
+ int ret = 0;
if( encoding < 0 ) {
encoding = 0;
if( !(fmt_ctx->flags & AVFMT_NOFILE) &&
- avio_open(&fmt_ctx->pb, fmt_ctx->filename, AVIO_FLAG_WRITE) < 0 ) {
- fprintf(stderr, "FFMPEG::encode_activate: err opening : %s\n",
+ (ret=avio_open(&fmt_ctx->pb, fmt_ctx->filename, AVIO_FLAG_WRITE)) < 0 ) {
+ ff_err(ret, "FFMPEG::encode_activate: err opening : %s\n",
fmt_ctx->filename);
return 1;
}
char option_path[BCTEXTLEN];
set_option_path(option_path, "format/%s", file_format);
read_options(option_path, fopts);
- int ret = avformat_write_header(fmt_ctx, &fopts);
+ ret = avformat_write_header(fmt_ctx, &fopts);
av_dict_free(&fopts);
if( ret < 0 ) {
- fprintf(stderr, "FFMPEG::encode_activate: write header failed %s\n",
+ ff_err(ret, "FFMPEG::encode_activate: write header failed %s\n",
fmt_ctx->filename);
return 1;
}
FFAudioStream *aud = ffaudio[aidx];
if( aud->load(pos, len) < len ) return -1;
int ch = astrm_index[chn].st_ch;
- return aud->history.read(samples,len,ch);
+ return aud->read(samples,len,ch);
}
int FFMPEG::decode(int layer, int64_t pos, VFrame *vframe)
int FFMPEG::mux_audio(FFrame *frm)
{
FFPacket pkt;
- AVStream *st = frm->fst->st;
- AVCodecContext *ctx = st->codec;
+ FFStream *fst = frm->fst;
+ AVCodecContext *ctx = fst->st->codec;
AVFrame *frame = *frm;
AVRational tick_rate = {1, ctx->sample_rate};
frame->pts = av_rescale_q(frm->position, tick_rate, ctx->time_base);
int got_packet = 0;
- int ret = avcodec_encode_audio2(ctx, pkt, frame, &got_packet);
- if( ret >= 0 && got_packet ) {
- frm->fst->bs_filter(pkt);
- av_packet_rescale_ts(pkt, ctx->time_base, st->time_base);
- pkt->stream_index = st->index;
- ret = av_interleaved_write_frame(fmt_ctx, pkt);
- }
+ int ret = fst->encode_frame(pkt, frame, got_packet);
+ if( ret >= 0 && got_packet )
+ ret = fst->write_packet(pkt);
if( ret < 0 )
ff_err(ret, "FFMPEG::mux_audio");
return ret >= 0 ? 0 : 1;
int FFMPEG::mux_video(FFrame *frm)
{
FFPacket pkt;
- AVStream *st = frm->fst->st;
+ FFStream *fst = frm->fst;
AVFrame *frame = *frm;
frame->pts = frm->position;
int ret = 1, got_packet = 0;
if( fmt_ctx->oformat->flags & AVFMT_RAWPICTURE ) {
/* a hack to avoid data copy with some raw video muxers */
pkt->flags |= AV_PKT_FLAG_KEY;
- pkt->stream_index = st->index;
+ pkt->stream_index = fst->st->index;
AVPicture *picture = (AVPicture *)frame;
pkt->data = (uint8_t *)picture;
pkt->size = sizeof(AVPicture);
got_packet = 1;
}
else
- ret = avcodec_encode_video2(st->codec, pkt, frame, &got_packet);
- if( ret >= 0 && got_packet ) {
- frm->fst->bs_filter(pkt);
- av_packet_rescale_ts(pkt, st->codec->time_base, st->time_base);
- pkt->stream_index = st->index;
- ret = av_interleaved_write_frame(fmt_ctx, pkt);
- }
+ ret = fst->encode_frame(pkt, frame, got_packet);
+ if( ret >= 0 && got_packet )
+ ret = fst->write_packet(pkt);
if( ret < 0 )
ff_err(ret, "FFMPEG::mux_video");
return ret >= 0 ? 0 : 1;
if( !done ) mux();
}
mux();
+ for( int i=0; i<ffaudio.size(); ++i )
+ ffaudio[i]->flush();
+ for( int i=0; i<ffvideo.size(); ++i )
+ ffvideo[i]->flush();
}