mirror of https://github.com/mpv-player/mpv
ao_lavc, vo_lavc: Migrate to new encoding API.
Also marked some places for possible later refactoring, as they became quite similar in this commit.
This commit is contained in:
parent
c5094206ce
commit
acb74236ac
|
@ -39,8 +39,6 @@
|
|||
#include "common/encode_lavc.h"
|
||||
|
||||
struct priv {
|
||||
uint8_t *buffer;
|
||||
size_t buffer_size;
|
||||
AVStream *stream;
|
||||
AVCodecContext *codec;
|
||||
int pcmhack;
|
||||
|
@ -146,18 +144,10 @@ static int init(struct ao *ao)
|
|||
if (ac->codec->frame_size <= 1)
|
||||
ac->pcmhack = av_get_bits_per_sample(ac->codec->codec_id) / 8;
|
||||
|
||||
if (ac->pcmhack) {
|
||||
if (ac->pcmhack)
|
||||
ac->aframesize = 16384; // "enough"
|
||||
ac->buffer_size =
|
||||
ac->aframesize * ac->pcmhack * ao->channels.num * 2 + 200;
|
||||
} else {
|
||||
else
|
||||
ac->aframesize = ac->codec->frame_size;
|
||||
ac->buffer_size =
|
||||
ac->aframesize * ac->sample_size * ao->channels.num * 2 + 200;
|
||||
}
|
||||
if (ac->buffer_size < FF_MIN_BUFFER_SIZE)
|
||||
ac->buffer_size = FF_MIN_BUFFER_SIZE;
|
||||
ac->buffer = talloc_size(ac, ac->buffer_size);
|
||||
|
||||
// enough frames for at least 0.25 seconds
|
||||
ac->framecount = ceil(ao->samplerate * 0.25 / ac->aframesize);
|
||||
|
@ -182,7 +172,7 @@ fail:
|
|||
}
|
||||
|
||||
// close audio device
|
||||
static int encode(struct ao *ao, double apts, void **data);
|
||||
static void encode(struct ao *ao, double apts, void **data);
|
||||
static void uninit(struct ao *ao)
|
||||
{
|
||||
struct priv *ac = ao->priv;
|
||||
|
@ -199,12 +189,12 @@ static void uninit(struct ao *ao)
|
|||
return;
|
||||
}
|
||||
|
||||
if (ac->buffer) {
|
||||
if (ac->stream) {
|
||||
double outpts = ac->expected_next_pts;
|
||||
if (!ectx->options->rawts && ectx->options->copyts)
|
||||
outpts += ectx->discontinuity_pts_offset;
|
||||
outpts += encode_lavc_getoffset(ectx, ac->codec);
|
||||
while (encode(ao, outpts, NULL) > 0) ;
|
||||
encode(ao, outpts, NULL);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&ectx->lock);
|
||||
|
@ -220,24 +210,130 @@ static int get_space(struct ao *ao)
|
|||
return ac->aframesize * ac->framecount;
|
||||
}
|
||||
|
||||
// must get exactly ac->aframesize amount of data
|
||||
static int encode(struct ao *ao, double apts, void **data)
|
||||
static void write_packet(struct ao *ao, AVPacket *packet)
|
||||
{
|
||||
// TODO: Can we unify this with the equivalent video code path?
|
||||
struct priv *ac = ao->priv;
|
||||
|
||||
packet->stream_index = ac->stream->index;
|
||||
if (packet->pts != AV_NOPTS_VALUE) {
|
||||
packet->pts = av_rescale_q(packet->pts,
|
||||
ac->codec->time_base,
|
||||
ac->stream->time_base);
|
||||
} else {
|
||||
// Do we need this at all? Better be safe than sorry...
|
||||
MP_WARN(ao, "encoder lost pts, why?\n");
|
||||
if (ac->savepts != MP_NOPTS_VALUE) {
|
||||
packet->pts = av_rescale_q(ac->savepts,
|
||||
ac->codec->time_base,
|
||||
ac->stream->time_base);
|
||||
}
|
||||
}
|
||||
if (packet->dts != AV_NOPTS_VALUE) {
|
||||
packet->dts = av_rescale_q(packet->dts,
|
||||
ac->codec->time_base,
|
||||
ac->stream->time_base);
|
||||
}
|
||||
if (packet->duration > 0) {
|
||||
packet->duration = av_rescale_q(packet->duration,
|
||||
ac->codec->time_base,
|
||||
ac->stream->time_base);
|
||||
}
|
||||
|
||||
ac->savepts = AV_NOPTS_VALUE;
|
||||
|
||||
if (encode_lavc_write_frame(ao->encode_lavc_ctx,
|
||||
ac->stream, packet) < 0) {
|
||||
MP_ERR(ao, "error writing at %d %d/%d\n",
|
||||
(int) packet->pts,
|
||||
ac->stream->time_base.num,
|
||||
ac->stream->time_base.den);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void encode_audio_and_write(struct ao *ao, AVFrame *frame)
|
||||
{
|
||||
// TODO: Can we unify this with the equivalent video code path?
|
||||
struct priv *ac = ao->priv;
|
||||
AVPacket packet = {0};
|
||||
|
||||
#if HAVE_AVCODEC_NEW_CODEC_API
|
||||
int status = avcodec_send_frame(ac->codec, frame);
|
||||
if (status < 0) {
|
||||
MP_ERR(ao, "error encoding at %d %d/%d\n",
|
||||
frame ? (int) frame->pts : -1,
|
||||
ac->codec->time_base.num,
|
||||
ac->codec->time_base.den);
|
||||
return;
|
||||
}
|
||||
for (;;) {
|
||||
av_init_packet(&packet);
|
||||
status = avcodec_receive_packet(ac->codec, &packet);
|
||||
if (status == AVERROR(EAGAIN)) { // No more packets for now.
|
||||
if (frame == NULL) {
|
||||
MP_ERR(ao, "sent flush frame, got EAGAIN");
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (status == AVERROR_EOF) { // No more packets, ever.
|
||||
if (frame != NULL) {
|
||||
MP_ERR(ao, "sent audio frame, got EOF");
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (status < 0) {
|
||||
MP_ERR(ao, "error encoding at %d %d/%d\n",
|
||||
frame ? (int) frame->pts : -1,
|
||||
ac->codec->time_base.num,
|
||||
ac->codec->time_base.den);
|
||||
break;
|
||||
}
|
||||
if (frame) {
|
||||
if (ac->savepts == AV_NOPTS_VALUE)
|
||||
ac->savepts = frame->pts;
|
||||
}
|
||||
encode_lavc_write_stats(ao->encode_lavc_ctx, ac->codec);
|
||||
write_packet(ao, &packet);
|
||||
av_packet_unref(&packet);
|
||||
}
|
||||
#else
|
||||
av_init_packet(&packet);
|
||||
int got_packet = 0;
|
||||
int status = avcodec_encode_audio2(ac->codec, &packet, frame, &got_packet);
|
||||
if (status < 0) {
|
||||
MP_ERR(ao, "error encoding at %d %d/%d\n",
|
||||
frame ? (int) frame->pts : -1,
|
||||
ac->codec->time_base.num,
|
||||
ac->codec->time_base.den);
|
||||
return;
|
||||
}
|
||||
if (!got_packet) {
|
||||
return;
|
||||
}
|
||||
if (frame) {
|
||||
if (ac->savepts == AV_NOPTS_VALUE)
|
||||
ac->savepts = frame->pts;
|
||||
}
|
||||
encode_lavc_write_stats(ao->encode_lavc_ctx, ac->codec);
|
||||
write_packet(ao, &packet);
|
||||
av_packet_unref(&packet);
|
||||
#endif
|
||||
}
|
||||
|
||||
// must get exactly ac->aframesize amount of data
|
||||
static void encode(struct ao *ao, double apts, void **data)
|
||||
{
|
||||
AVPacket packet;
|
||||
struct priv *ac = ao->priv;
|
||||
struct encode_lavc_context *ectx = ao->encode_lavc_ctx;
|
||||
double realapts = ac->aframecount * (double) ac->aframesize /
|
||||
ao->samplerate;
|
||||
int status, gotpacket;
|
||||
|
||||
ac->aframecount++;
|
||||
|
||||
if (data)
|
||||
ectx->audio_pts_offset = realapts - apts;
|
||||
|
||||
av_init_packet(&packet);
|
||||
packet.data = ac->buffer;
|
||||
packet.size = ac->buffer_size;
|
||||
if(data) {
|
||||
AVFrame *frame = av_frame_alloc();
|
||||
frame->format = af_to_avformat(ao->format);
|
||||
|
@ -270,64 +366,11 @@ static int encode(struct ao *ao, double apts, void **data)
|
|||
ac->lastpts = frame_pts;
|
||||
|
||||
frame->quality = ac->codec->global_quality;
|
||||
status = avcodec_encode_audio2(ac->codec, &packet, frame, &gotpacket);
|
||||
|
||||
if (!status) {
|
||||
if (ac->savepts == AV_NOPTS_VALUE)
|
||||
ac->savepts = frame->pts;
|
||||
}
|
||||
|
||||
encode_audio_and_write(ao, frame);
|
||||
av_frame_free(&frame);
|
||||
}
|
||||
else
|
||||
{
|
||||
status = avcodec_encode_audio2(ac->codec, &packet, NULL, &gotpacket);
|
||||
}
|
||||
|
||||
if(status) {
|
||||
MP_ERR(ao, "error encoding\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!gotpacket)
|
||||
return 0;
|
||||
|
||||
MP_DBG(ao, "got pts %f (playback time: %f); out size: %d\n",
|
||||
apts, realapts, packet.size);
|
||||
|
||||
encode_lavc_write_stats(ao->encode_lavc_ctx, ac->codec);
|
||||
|
||||
packet.stream_index = ac->stream->index;
|
||||
|
||||
// Do we need this at all? Better be safe than sorry...
|
||||
if (packet.pts == AV_NOPTS_VALUE) {
|
||||
MP_WARN(ao, "encoder lost pts, why?\n");
|
||||
if (ac->savepts != MP_NOPTS_VALUE)
|
||||
packet.pts = ac->savepts;
|
||||
}
|
||||
|
||||
if (packet.pts != AV_NOPTS_VALUE)
|
||||
packet.pts = av_rescale_q(packet.pts, ac->codec->time_base,
|
||||
ac->stream->time_base);
|
||||
|
||||
if (packet.dts != AV_NOPTS_VALUE)
|
||||
packet.dts = av_rescale_q(packet.dts, ac->codec->time_base,
|
||||
ac->stream->time_base);
|
||||
|
||||
if(packet.duration > 0)
|
||||
packet.duration = av_rescale_q(packet.duration, ac->codec->time_base,
|
||||
ac->stream->time_base);
|
||||
|
||||
ac->savepts = AV_NOPTS_VALUE;
|
||||
|
||||
if (encode_lavc_write_frame(ao->encode_lavc_ctx, ac->stream, &packet) < 0) {
|
||||
MP_ERR(ao, "error writing at %f %f/%f\n",
|
||||
realapts, (double) ac->stream->time_base.num,
|
||||
(double) ac->stream->time_base.den);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return packet.size;
|
||||
encode_audio_and_write(ao, NULL);
|
||||
}
|
||||
|
||||
// this should round samples down to frame sizes
|
||||
|
@ -492,3 +535,5 @@ const struct ao_driver audio_out_lavc = {
|
|||
.play = play,
|
||||
.drain = drain,
|
||||
};
|
||||
|
||||
// vim: sw=4 ts=4 et tw=80
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "common/common.h"
|
||||
#include "options/options.h"
|
||||
#include "video/fmt-conversion.h"
|
||||
|
@ -34,8 +36,6 @@
|
|||
#include "sub/osd.h"
|
||||
|
||||
struct priv {
|
||||
uint8_t *buffer;
|
||||
size_t buffer_size;
|
||||
AVStream *stream;
|
||||
AVCodecContext *codec;
|
||||
int have_first_packet;
|
||||
|
@ -161,14 +161,6 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
|
|||
if (encode_lavc_open_codec(vo->encode_lavc_ctx, vc->codec) < 0)
|
||||
goto error;
|
||||
|
||||
vc->buffer_size = 6 * width * height + 200;
|
||||
if (vc->buffer_size < FF_MIN_BUFFER_SIZE)
|
||||
vc->buffer_size = FF_MIN_BUFFER_SIZE;
|
||||
if (vc->buffer_size < sizeof(AVPicture))
|
||||
vc->buffer_size = sizeof(AVPicture);
|
||||
|
||||
vc->buffer = talloc_size(vc, vc->buffer_size);
|
||||
|
||||
done:
|
||||
pthread_mutex_unlock(&vo->encode_lavc_ctx->lock);
|
||||
return 0;
|
||||
|
@ -194,82 +186,120 @@ static int query_format(struct vo *vo, int format)
|
|||
return flags;
|
||||
}
|
||||
|
||||
static void write_packet(struct vo *vo, int size, AVPacket *packet)
|
||||
static void write_packet(struct vo *vo, AVPacket *packet)
|
||||
{
|
||||
struct priv *vc = vo->priv;
|
||||
|
||||
if (size < 0) {
|
||||
MP_ERR(vo, "error encoding\n");
|
||||
packet->stream_index = vc->stream->index;
|
||||
if (packet->pts != AV_NOPTS_VALUE) {
|
||||
packet->pts = av_rescale_q(packet->pts,
|
||||
vc->codec->time_base,
|
||||
vc->stream->time_base);
|
||||
} else {
|
||||
MP_VERBOSE(vo, "codec did not provide pts\n");
|
||||
packet->pts = av_rescale_q(vc->lastipts,
|
||||
vc->worst_time_base,
|
||||
vc->stream->time_base);
|
||||
}
|
||||
if (packet->dts != AV_NOPTS_VALUE) {
|
||||
packet->dts = av_rescale_q(packet->dts,
|
||||
vc->codec->time_base,
|
||||
vc->stream->time_base);
|
||||
}
|
||||
if (packet->duration > 0) {
|
||||
packet->duration = av_rescale_q(packet->duration,
|
||||
vc->codec->time_base,
|
||||
vc->stream->time_base);
|
||||
} else {
|
||||
// HACK: libavformat calculates dts wrong if the initial packet
|
||||
// duration is not set, but ONLY if the time base is "high" and if we
|
||||
// have b-frames!
|
||||
if (!packet->duration)
|
||||
if (!vc->have_first_packet)
|
||||
if (vc->codec->has_b_frames
|
||||
|| vc->codec->max_b_frames)
|
||||
if (vc->stream->time_base.num * 1000LL <=
|
||||
vc->stream->time_base.den)
|
||||
packet->duration = FFMAX(1, av_rescale_q(1,
|
||||
vc->codec->time_base, vc->stream->time_base));
|
||||
}
|
||||
|
||||
if (encode_lavc_write_frame(vo->encode_lavc_ctx,
|
||||
vc->stream, packet) < 0) {
|
||||
MP_ERR(vo, "error writing at %d %d/%d\n",
|
||||
(int) packet->pts,
|
||||
vc->stream->time_base.num,
|
||||
vc->stream->time_base.den);
|
||||
return;
|
||||
}
|
||||
|
||||
if (size > 0) {
|
||||
packet->stream_index = vc->stream->index;
|
||||
if (packet->pts != AV_NOPTS_VALUE) {
|
||||
packet->pts = av_rescale_q(packet->pts,
|
||||
vc->codec->time_base,
|
||||
vc->stream->time_base);
|
||||
} else {
|
||||
MP_VERBOSE(vo, "codec did not provide pts\n");
|
||||
packet->pts = av_rescale_q(vc->lastipts, vc->worst_time_base,
|
||||
vc->stream->time_base);
|
||||
}
|
||||
if (packet->dts != AV_NOPTS_VALUE) {
|
||||
packet->dts = av_rescale_q(packet->dts,
|
||||
vc->codec->time_base,
|
||||
vc->stream->time_base);
|
||||
}
|
||||
if (packet->duration > 0) {
|
||||
packet->duration = av_rescale_q(packet->duration,
|
||||
vc->codec->time_base,
|
||||
vc->stream->time_base);
|
||||
} else {
|
||||
// HACK: libavformat calculates dts wrong if the initial packet
|
||||
// duration is not set, but ONLY if the time base is "high" and if we
|
||||
// have b-frames!
|
||||
if (!packet->duration)
|
||||
if (!vc->have_first_packet)
|
||||
if (vc->codec->has_b_frames
|
||||
|| vc->codec->max_b_frames)
|
||||
if (vc->stream->time_base.num * 1000LL <=
|
||||
vc->stream->time_base.den)
|
||||
packet->duration = FFMAX(1, av_rescale_q(1,
|
||||
vc->codec->time_base, vc->stream->time_base));
|
||||
}
|
||||
|
||||
if (encode_lavc_write_frame(vo->encode_lavc_ctx,
|
||||
vc->stream, packet) < 0) {
|
||||
MP_ERR(vo, "error writing\n");
|
||||
return;
|
||||
}
|
||||
|
||||
vc->have_first_packet = 1;
|
||||
}
|
||||
vc->have_first_packet = 1;
|
||||
}
|
||||
|
||||
static int encode_video(struct vo *vo, AVFrame *frame, AVPacket *packet)
|
||||
static void encode_video_and_write(struct vo *vo, AVFrame *frame)
|
||||
{
|
||||
struct priv *vc = vo->priv;
|
||||
int got_packet = 0;
|
||||
int status = avcodec_encode_video2(vc->codec, packet,
|
||||
frame, &got_packet);
|
||||
int size = (status < 0) ? status : got_packet ? packet->size : 0;
|
||||
AVPacket packet = {0};
|
||||
|
||||
if (frame)
|
||||
MP_DBG(vo, "got pts %f; out size: %d\n",
|
||||
frame->pts * (double) vc->codec->time_base.num /
|
||||
(double) vc->codec->time_base.den, size);
|
||||
|
||||
if (got_packet)
|
||||
#if HAVE_AVCODEC_NEW_CODEC_API
|
||||
int status = avcodec_send_frame(vc->codec, frame);
|
||||
if (status < 0) {
|
||||
MP_ERR(vo, "error encoding at %d %d/%d\n",
|
||||
frame ? (int) frame->pts : -1,
|
||||
vc->codec->time_base.num,
|
||||
vc->codec->time_base.den);
|
||||
return;
|
||||
}
|
||||
for (;;) {
|
||||
av_init_packet(&packet);
|
||||
status = avcodec_receive_packet(vc->codec, &packet);
|
||||
if (status == AVERROR(EAGAIN)) { // No more packets for now.
|
||||
if (frame == NULL) {
|
||||
MP_ERR(vo, "sent flush frame, got EAGAIN");
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (status == AVERROR_EOF) { // No more packets, ever.
|
||||
if (frame != NULL) {
|
||||
MP_ERR(vo, "sent image frame, got EOF");
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (status < 0) {
|
||||
MP_ERR(vo, "error encoding at %d %d/%d\n",
|
||||
frame ? (int) frame->pts : -1,
|
||||
vc->codec->time_base.num,
|
||||
vc->codec->time_base.den);
|
||||
break;
|
||||
}
|
||||
encode_lavc_write_stats(vo->encode_lavc_ctx, vc->codec);
|
||||
return size;
|
||||
write_packet(vo, &packet);
|
||||
av_packet_unref(&packet);
|
||||
}
|
||||
#else
|
||||
av_init_packet(&packet);
|
||||
int got_packet = 0;
|
||||
int status = avcodec_encode_video2(vc->codec, &packet, frame, &got_packet);
|
||||
if (status < 0) {
|
||||
MP_ERR(vo, "error encoding at %d %d/%d\n",
|
||||
frame ? (int) frame->pts : -1,
|
||||
vc->codec->time_base.num,
|
||||
vc->codec->time_base.den);
|
||||
return;
|
||||
}
|
||||
if (!got_packet) {
|
||||
return;
|
||||
}
|
||||
encode_lavc_write_stats(vo->encode_lavc_ctx, vc->codec);
|
||||
write_packet(vo, &packet);
|
||||
av_packet_unref(&packet);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void draw_image_unlocked(struct vo *vo, mp_image_t *mpi)
|
||||
{
|
||||
struct priv *vc = vo->priv;
|
||||
struct encode_lavc_context *ectx = vo->encode_lavc_ctx;
|
||||
int size;
|
||||
AVCodecContext *avc;
|
||||
int64_t frameipts;
|
||||
double nextpts;
|
||||
|
@ -398,7 +428,6 @@ static void draw_image_unlocked(struct vo *vo, mp_image_t *mpi)
|
|||
// we have a valid image in lastimg
|
||||
while (vc->lastimg && vc->lastipts < frameipts) {
|
||||
int64_t thisduration = vc->harddup ? 1 : (frameipts - vc->lastipts);
|
||||
AVPacket packet;
|
||||
|
||||
// we will ONLY encode this frame if it can be encoded at at least
|
||||
// vc->mindeltapts after the last encoded frame!
|
||||
|
@ -417,20 +446,13 @@ static void draw_image_unlocked(struct vo *vo, mp_image_t *mpi)
|
|||
// this is a nop, unless the worst time base is the STREAM time base
|
||||
frame->pts = av_rescale_q(vc->lastipts + skipframes,
|
||||
vc->worst_time_base, avc->time_base);
|
||||
|
||||
frame->pict_type = 0; // keep this at unknown/undefined
|
||||
|
||||
frame->quality = avc->global_quality;
|
||||
encode_video_and_write(vo, frame);
|
||||
av_frame_free(&frame);
|
||||
|
||||
av_init_packet(&packet);
|
||||
packet.data = vc->buffer;
|
||||
packet.size = vc->buffer_size;
|
||||
size = encode_video(vo, frame, &packet);
|
||||
write_packet(vo, size, &packet);
|
||||
++vc->lastdisplaycount;
|
||||
vc->lastencodedipts = vc->lastipts + skipframes;
|
||||
|
||||
av_frame_free(&frame);
|
||||
}
|
||||
|
||||
vc->lastipts += thisduration;
|
||||
|
@ -439,14 +461,7 @@ static void draw_image_unlocked(struct vo *vo, mp_image_t *mpi)
|
|||
|
||||
if (!mpi) {
|
||||
// finish encoding
|
||||
do {
|
||||
AVPacket packet;
|
||||
av_init_packet(&packet);
|
||||
packet.data = vc->buffer;
|
||||
packet.size = vc->buffer_size;
|
||||
size = encode_video(vo, NULL, &packet);
|
||||
write_packet(vo, size, &packet);
|
||||
} while (size > 0);
|
||||
encode_video_and_write(vo, NULL);
|
||||
} else {
|
||||
if (frameipts >= vc->lastframeipts) {
|
||||
if (vc->lastframeipts != AV_NOPTS_VALUE && vc->lastdisplaycount != 1)
|
||||
|
|
Loading…
Reference in New Issue