2.5 Audio:
- recode of the whole sequencer audio handling - encode audio flag removed, instead you choose None as audio codec, added None for video codec too - ffmpeg formats/codecs: enabled: theora, ogg, vorbis; added: matroska, flac (not working, who can fix?), mp3, wav - sequencer wave drawing - volume animation (now also working when mixing down to a file!) - made sequencer strip position and length values unanimatable
This commit is contained in:
@@ -15,7 +15,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef WITH_FFMPEG
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
@@ -78,11 +77,10 @@ extern void do_init_ffmpeg();
|
||||
|
||||
static int ffmpeg_type = 0;
|
||||
static int ffmpeg_codec = CODEC_ID_MPEG4;
|
||||
static int ffmpeg_audio_codec = CODEC_ID_MP2;
|
||||
static int ffmpeg_audio_codec = CODEC_ID_NONE;
|
||||
static int ffmpeg_video_bitrate = 1150;
|
||||
static int ffmpeg_audio_bitrate = 128;
|
||||
static int ffmpeg_gop_size = 12;
|
||||
static int ffmpeg_multiplex_audio = 1;
|
||||
static int ffmpeg_autosplit = 0;
|
||||
static int ffmpeg_autosplit_count = 0;
|
||||
|
||||
@@ -99,6 +97,7 @@ static uint8_t* audio_input_buffer = 0;
|
||||
static int audio_input_frame_size = 0;
|
||||
static uint8_t* audio_output_buffer = 0;
|
||||
static int audio_outbuf_size = 0;
|
||||
static double audio_time = 0.0f;
|
||||
|
||||
static AUD_Device* audio_mixdown_device = 0;
|
||||
|
||||
@@ -130,30 +129,43 @@ static int write_audio_frame(void)
|
||||
{
|
||||
AVCodecContext* c = NULL;
|
||||
AVPacket pkt;
|
||||
AVStream* str = audio_stream;
|
||||
|
||||
c = get_codec_from_stream(audio_stream);
|
||||
|
||||
if(audio_mixdown_device)
|
||||
AUD_readDevice(audio_mixdown_device, audio_input_buffer, audio_input_frame_size);
|
||||
|
||||
av_init_packet(&pkt);
|
||||
pkt.size = 0;
|
||||
|
||||
AUD_readDevice(audio_mixdown_device, audio_input_buffer, audio_input_frame_size);
|
||||
audio_time += (double) audio_input_frame_size / (double) c->sample_rate;
|
||||
|
||||
pkt.size = avcodec_encode_audio(c, audio_output_buffer,
|
||||
audio_outbuf_size,
|
||||
audio_outbuf_size,
|
||||
(short*) audio_input_buffer);
|
||||
|
||||
if(pkt.size <= 0)
|
||||
{
|
||||
// XXX error("Error writing audio packet");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pkt.data = audio_output_buffer;
|
||||
|
||||
if(c->coded_frame && c->coded_frame->pts != AV_NOPTS_VALUE)
|
||||
{
|
||||
#ifdef FFMPEG_CODEC_TIME_BASE
|
||||
pkt.pts = av_rescale_q(c->coded_frame->pts,
|
||||
c->time_base, audio_stream->time_base);
|
||||
pkt.pts = av_rescale_q(c->coded_frame->pts,
|
||||
c->time_base, audio_stream->time_base);
|
||||
#else
|
||||
pkt.pts = c->coded_frame->pts;
|
||||
pkt.pts = c->coded_frame->pts;
|
||||
#endif
|
||||
fprintf(stderr, "Audio Frame PTS: %d\n", (int)pkt.pts);
|
||||
fprintf(stderr, "Audio Frame PTS: %d\n", (int)pkt.pts);
|
||||
}
|
||||
|
||||
pkt.stream_index = audio_stream->index;
|
||||
pkt.flags |= PKT_FLAG_KEY;
|
||||
if (av_interleaved_write_frame(outfile, &pkt) != 0) {
|
||||
//XXX error("Error writing audio packet");
|
||||
// XXX error("Error writing audio packet");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@@ -233,6 +245,14 @@ static const char** get_file_extensions(int format)
|
||||
static const char * rv[] = { ".ogg", ".ogv", NULL };
|
||||
return rv;
|
||||
}
|
||||
case FFMPEG_MP3: {
|
||||
static const char * rv[] = { ".mp3", NULL };
|
||||
return rv;
|
||||
}
|
||||
case FFMPEG_WAV: {
|
||||
static const char * rv[] = { ".wav", NULL };
|
||||
return rv;
|
||||
}
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
@@ -563,6 +583,7 @@ static AVStream* alloc_audio_stream(RenderData *rd, int codec_id, AVFormatContex
|
||||
|
||||
c->sample_rate = rd->ffcodecdata.audio_mixrate;
|
||||
c->bit_rate = ffmpeg_audio_bitrate*1000;
|
||||
c->sample_fmt = SAMPLE_FMT_S16;
|
||||
c->channels = 2;
|
||||
codec = avcodec_find_encoder(c->codec_id);
|
||||
if (!codec) {
|
||||
@@ -577,40 +598,22 @@ static AVStream* alloc_audio_stream(RenderData *rd, int codec_id, AVFormatContex
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* FIXME: Should be user configurable */
|
||||
if (ffmpeg_type == FFMPEG_DV) {
|
||||
/* this is a hack around the poor ffmpeg dv multiplexer. */
|
||||
/* only fixes PAL for now
|
||||
(NTSC is a lot more complicated here...)! */
|
||||
audio_outbuf_size = 7680;
|
||||
} else {
|
||||
audio_outbuf_size = 10000;
|
||||
}
|
||||
audio_outbuf_size = FF_MIN_BUFFER_SIZE;
|
||||
|
||||
audio_output_buffer = (uint8_t*)MEM_mallocN(
|
||||
audio_outbuf_size, "FFMPEG audio encoder input buffer");
|
||||
|
||||
/* ugly hack for PCM codecs */
|
||||
|
||||
if (c->frame_size <= 1) {
|
||||
audio_input_frame_size = audio_outbuf_size / c->channels;
|
||||
switch(c->codec_id) {
|
||||
case CODEC_ID_PCM_S16LE:
|
||||
case CODEC_ID_PCM_S16BE:
|
||||
case CODEC_ID_PCM_U16LE:
|
||||
case CODEC_ID_PCM_U16BE:
|
||||
audio_input_frame_size >>= 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if((c->codec_id >= CODEC_ID_PCM_S16LE) && (c->codec_id <= CODEC_ID_PCM_DVD))
|
||||
audio_input_frame_size = audio_outbuf_size * 8 / c->bits_per_coded_sample / c->channels;
|
||||
else
|
||||
audio_input_frame_size = c->frame_size;
|
||||
}
|
||||
|
||||
audio_input_buffer = (uint8_t*)MEM_mallocN(
|
||||
audio_input_frame_size * sizeof(short) * c->channels,
|
||||
audio_input_frame_size * c->channels * sizeof(int16_t),
|
||||
"FFMPEG audio encoder output buffer");
|
||||
|
||||
audio_time = 0.0f;
|
||||
|
||||
return st;
|
||||
}
|
||||
/* essential functions -- start, append, end */
|
||||
@@ -629,8 +632,6 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report
|
||||
ffmpeg_video_bitrate = rd->ffcodecdata.video_bitrate;
|
||||
ffmpeg_audio_bitrate = rd->ffcodecdata.audio_bitrate;
|
||||
ffmpeg_gop_size = rd->ffcodecdata.gop_size;
|
||||
ffmpeg_multiplex_audio = rd->ffcodecdata.flags
|
||||
& FFMPEG_MULTIPLEX_AUDIO;
|
||||
ffmpeg_autosplit = rd->ffcodecdata.flags
|
||||
& FFMPEG_AUTOSPLIT_OUTPUT;
|
||||
|
||||
@@ -641,12 +642,11 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report
|
||||
fprintf(stderr, "Starting output to %s(ffmpeg)...\n"
|
||||
" Using type=%d, codec=%d, audio_codec=%d,\n"
|
||||
" video_bitrate=%d, audio_bitrate=%d,\n"
|
||||
" gop_size=%d, multiplex=%d, autosplit=%d\n"
|
||||
" gop_size=%d, autosplit=%d\n"
|
||||
" render width=%d, render height=%d\n",
|
||||
name, ffmpeg_type, ffmpeg_codec, ffmpeg_audio_codec,
|
||||
ffmpeg_video_bitrate, ffmpeg_audio_bitrate,
|
||||
ffmpeg_gop_size, ffmpeg_multiplex_audio,
|
||||
ffmpeg_autosplit, rectx, recty);
|
||||
ffmpeg_gop_size, ffmpeg_autosplit, rectx, recty);
|
||||
|
||||
exts = get_file_extensions(ffmpeg_type);
|
||||
if (!exts) {
|
||||
@@ -667,7 +667,7 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report
|
||||
|
||||
of->oformat = fmt;
|
||||
of->packet_size= rd->ffcodecdata.mux_packet_size;
|
||||
if (ffmpeg_multiplex_audio) {
|
||||
if (ffmpeg_audio_codec != CODEC_ID_NONE) {
|
||||
of->mux_rate = rd->ffcodecdata.mux_rate;
|
||||
} else {
|
||||
of->mux_rate = 0;
|
||||
@@ -676,6 +676,8 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report
|
||||
of->preload = (int)(0.5*AV_TIME_BASE);
|
||||
of->max_delay = (int)(0.7*AV_TIME_BASE);
|
||||
|
||||
fmt->audio_codec = ffmpeg_audio_codec;
|
||||
|
||||
snprintf(of->filename, sizeof(of->filename), "%s", name);
|
||||
/* set the codec to the user's selection */
|
||||
switch(ffmpeg_type) {
|
||||
@@ -703,6 +705,11 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report
|
||||
case FFMPEG_FLV:
|
||||
fmt->video_codec = CODEC_ID_FLV1;
|
||||
break;
|
||||
case FFMPEG_MP3:
|
||||
fmt->audio_codec = CODEC_ID_MP3;
|
||||
case FFMPEG_WAV:
|
||||
fmt->video_codec = CODEC_ID_NONE;
|
||||
break;
|
||||
case FFMPEG_MPEG4:
|
||||
default:
|
||||
fmt->video_codec = CODEC_ID_MPEG4;
|
||||
@@ -723,30 +730,29 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report
|
||||
}
|
||||
}
|
||||
|
||||
fmt->audio_codec = ffmpeg_audio_codec;
|
||||
|
||||
if (ffmpeg_type == FFMPEG_DV) {
|
||||
fmt->audio_codec = CODEC_ID_PCM_S16LE;
|
||||
if (ffmpeg_multiplex_audio && rd->ffcodecdata.audio_mixrate != 48000) {
|
||||
if (ffmpeg_audio_codec != CODEC_ID_NONE && rd->ffcodecdata.audio_mixrate != 48000) {
|
||||
BKE_report(reports, RPT_ERROR, "FFMPEG only supports 48khz / stereo audio for DV!");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
video_stream = alloc_video_stream(rd, fmt->video_codec, of, rectx, recty);
|
||||
printf("alloc video stream %p\n", video_stream);
|
||||
if (!video_stream) {
|
||||
BKE_report(reports, RPT_ERROR, "Error initializing video stream.");
|
||||
return 0;
|
||||
if (fmt->video_codec != CODEC_ID_NONE) {
|
||||
video_stream = alloc_video_stream(rd, fmt->video_codec, of, rectx, recty);
|
||||
printf("alloc video stream %p\n", video_stream);
|
||||
if (!video_stream) {
|
||||
BKE_report(reports, RPT_ERROR, "Error initializing video stream.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ffmpeg_multiplex_audio) {
|
||||
|
||||
if (ffmpeg_audio_codec != CODEC_ID_NONE) {
|
||||
audio_stream = alloc_audio_stream(rd, fmt->audio_codec, of);
|
||||
if (!audio_stream) {
|
||||
BKE_report(reports, RPT_ERROR, "Error initializing audio stream.");
|
||||
return 0;
|
||||
}
|
||||
//XXX audiostream_play(SFRA, 0, 1);
|
||||
}
|
||||
if (av_set_parameters(of, NULL) < 0) {
|
||||
BKE_report(reports, RPT_ERROR, "Error setting output parameters.");
|
||||
@@ -818,14 +824,14 @@ int start_ffmpeg(struct Scene *scene, RenderData *rd, int rectx, int recty, Repo
|
||||
|
||||
success = start_ffmpeg_impl(rd, rectx, recty, reports);
|
||||
|
||||
if(ffmpeg_multiplex_audio && audio_stream)
|
||||
if(audio_stream)
|
||||
{
|
||||
AVCodecContext* c = get_codec_from_stream(audio_stream);
|
||||
AUD_DeviceSpecs specs;
|
||||
specs.channels = c->channels;
|
||||
specs.format = AUD_FORMAT_S16;
|
||||
specs.rate = rd->ffcodecdata.audio_mixrate;
|
||||
audio_mixdown_device = sound_mixdown(scene, specs, rd->sfra, rd->efra, rd->ffcodecdata.audio_volume);
|
||||
audio_mixdown_device = sound_mixdown(scene, specs, rd->sfra, rd->ffcodecdata.audio_volume);
|
||||
}
|
||||
|
||||
return success;
|
||||
@@ -833,21 +839,13 @@ int start_ffmpeg(struct Scene *scene, RenderData *rd, int rectx, int recty, Repo
|
||||
|
||||
void end_ffmpeg(void);
|
||||
|
||||
static void write_audio_frames()
|
||||
static void write_audio_frames(double to_pts)
|
||||
{
|
||||
int finished = 0;
|
||||
|
||||
while (ffmpeg_multiplex_audio && !finished) {
|
||||
double a_pts = ((double)audio_stream->pts.val
|
||||
* audio_stream->time_base.num
|
||||
/ audio_stream->time_base.den);
|
||||
double v_pts = ((double)video_stream->pts.val
|
||||
* video_stream->time_base.num
|
||||
/ video_stream->time_base.den);
|
||||
|
||||
if (a_pts < v_pts) {
|
||||
write_audio_frame();
|
||||
} else {
|
||||
while (audio_stream && !finished) {
|
||||
if((audio_time >= to_pts) ||
|
||||
(write_audio_frame())) {
|
||||
finished = 1;
|
||||
}
|
||||
}
|
||||
@@ -856,25 +854,31 @@ static void write_audio_frames()
|
||||
int append_ffmpeg(RenderData *rd, int frame, int *pixels, int rectx, int recty, ReportList *reports)
|
||||
{
|
||||
AVFrame* avframe;
|
||||
int success;
|
||||
int success = 1;
|
||||
|
||||
fprintf(stderr, "Writing frame %i, "
|
||||
"render width=%d, render height=%d\n", frame,
|
||||
rectx, recty);
|
||||
|
||||
write_audio_frames();
|
||||
// why is this done before writing the video frame and again at end_ffmpeg?
|
||||
// write_audio_frames(frame / (((double)rd->frs_sec) / rd->frs_sec_base));
|
||||
|
||||
avframe= generate_video_frame((unsigned char*) pixels, reports);
|
||||
success= (avframe && write_video_frame(rd, avframe, reports));
|
||||
if(video_stream)
|
||||
{
|
||||
avframe= generate_video_frame((unsigned char*) pixels, reports);
|
||||
success= (avframe && write_video_frame(rd, avframe, reports));
|
||||
|
||||
if (ffmpeg_autosplit) {
|
||||
if (url_ftell(OUTFILE_PB) > FFMPEG_AUTOSPLIT_SIZE) {
|
||||
end_ffmpeg();
|
||||
ffmpeg_autosplit_count++;
|
||||
success &= start_ffmpeg_impl(rd, rectx, recty, reports);
|
||||
if (ffmpeg_autosplit) {
|
||||
if (url_ftell(OUTFILE_PB) > FFMPEG_AUTOSPLIT_SIZE) {
|
||||
end_ffmpeg();
|
||||
ffmpeg_autosplit_count++;
|
||||
success &= start_ffmpeg_impl(rd, rectx, recty, reports);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
write_audio_frames(frame / (((double)rd->frs_sec) / rd->frs_sec_base));
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -885,9 +889,9 @@ void end_ffmpeg(void)
|
||||
|
||||
fprintf(stderr, "Closing ffmpeg...\n");
|
||||
|
||||
if (audio_stream && video_stream) {
|
||||
/* if (audio_stream) { SEE UPPER
|
||||
write_audio_frames();
|
||||
}
|
||||
}*/
|
||||
|
||||
if(audio_mixdown_device)
|
||||
{
|
||||
@@ -1259,8 +1263,8 @@ void ffmpeg_verify_image_type(RenderData *rd)
|
||||
}
|
||||
}
|
||||
|
||||
if(audio && rd->ffcodecdata.audio_codec <= 0) {
|
||||
rd->ffcodecdata.audio_codec = CODEC_ID_MP2;
|
||||
if(audio && rd->ffcodecdata.audio_codec < 0) {
|
||||
rd->ffcodecdata.audio_codec = CODEC_ID_NONE;
|
||||
rd->ffcodecdata.audio_bitrate = 128;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user