Re: [FFmpeg-devel] [PATCH] videotoolbox: allow to enable the async decoding.
On Thu, Sep 24, 2015 at 11:45:42AM +0200, Clément Bœsch wrote: > On Sun, Aug 09, 2015 at 01:11:44PM +0200, Sebastien Zwickert wrote: > > This patch allows to use the Videotoolbox API in asynchonous mode. > > Note that when using async decoding the user is responsible for > > releasing the async frame. > > Moreover, an option called videotoolbox_async was added to enable > > async decoding with ffmpeg CLI. > > > > --- > > ffmpeg.h | 1 + > > ffmpeg_opt.c | 1 + > > ffmpeg_videotoolbox.c | 69 + > > libavcodec/videotoolbox.c | 186 > > -- > > libavcodec/videotoolbox.h | 73 ++ > > 5 files changed, 294 insertions(+), 36 deletions(-) > > > > Ping and more comments on this: > > - is it meaningful to have a limit on the internal queue size (for memory > load for instance), or it's not relevant in case of hw accel? > To answer myself: a limit is actually required or it causes a crash at least on iOS after about 150 frames in the queue. Following is a hack I needed: diff --git a/libavcodec/videotoolbox.c b/libavcodec/videotoolbox.c index 9664f16..73b9023 100644 --- a/libavcodec/videotoolbox.c +++ b/libavcodec/videotoolbox.c @@ -222,6 +222,8 @@ static void videotoolbox_clear_queue(struct AVVideotoolboxContext *videotoolbox) av_videotoolbox_release_async_frame(top_frame); } +videotoolbox->nb_frames = 0; + videotoolbox_lock_operation(>queue_mutex, AV_LOCK_RELEASE); } @@ -379,6 +381,9 @@ static void videotoolbox_decoder_callback(void *opaque, videotoolbox_lock_operation(>queue_mutex, AV_LOCK_OBTAIN); +while (videotoolbox->nb_frames == 16) +pthread_cond_wait(>queue_reduce, videotoolbox->queue_mutex); + queue_walker = videotoolbox->queue; if (!queue_walker || (new_frame->pts < queue_walker->pts)) { @@ -401,6 +406,7 @@ static void videotoolbox_decoder_callback(void *opaque, } } +videotoolbox->nb_frames++; videotoolbox_lock_operation(>queue_mutex, AV_LOCK_RELEASE); } } @@ -617,6 +623,8 @@ static int videotoolbox_default_init(AVCodecContext *avctx) return -1; videotoolbox_lock_operation(>queue_mutex, AV_LOCK_CREATE); + +pthread_cond_init(>queue_reduce, NULL); } switch( avctx->codec_id ) { @@ -700,6 +708,8 @@ static void videotoolbox_default_free(AVCodecContext *avctx) videotoolbox_clear_queue(videotoolbox); +pthread_cond_destroy(>queue_reduce); + if (videotoolbox->queue_mutex != NULL) videotoolbox_lock_operation(>queue_mutex, AV_LOCK_DESTROY); } @@ -826,8 +836,11 @@ AVVideotoolboxAsyncFrame *av_videotoolbox_pop_async_frame(AVVideotoolboxContext videotoolbox_lock_operation(>queue_mutex, AV_LOCK_OBTAIN); top_frame = videotoolbox->queue; videotoolbox->queue = top_frame->next_frame; +videotoolbox->nb_frames--; videotoolbox_lock_operation(>queue_mutex, AV_LOCK_RELEASE); +pthread_cond_signal(>queue_reduce); + return top_frame; } diff --git a/libavcodec/videotoolbox.h b/libavcodec/videotoolbox.h index b5bf030..db41b4a 100644 --- a/libavcodec/videotoolbox.h +++ b/libavcodec/videotoolbox.h @@ -113,6 +113,9 @@ typedef struct AVVideotoolboxContext { */ void *queue_mutex; +pthread_cond_t queue_reduce; +int nb_frames; + } AVVideotoolboxContext; /** > - isn't it missing a flush mechanism so seeking can be accurate? videotoolbox_clear_queue() should probably be exported [...] But again all of this is more in the spirit of a proper async API... BTW, I can confirm it fixes the playback speed on these devices. -- Clément B. signature.asc Description: PGP signature ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Re: [FFmpeg-devel] [PATCH] videotoolbox: allow to enable the async decoding.
On Sun, Aug 09, 2015 at 01:11:44PM +0200, Sebastien Zwickert wrote: > This patch allows to use the Videotoolbox API in asynchonous mode. > Note that when using async decoding the user is responsible for > releasing the async frame. > Moreover, an option called videotoolbox_async was added to enable > async decoding with ffmpeg CLI. > > --- > ffmpeg.h | 1 + > ffmpeg_opt.c | 1 + > ffmpeg_videotoolbox.c | 69 + > libavcodec/videotoolbox.c | 186 > -- > libavcodec/videotoolbox.h | 73 ++ > 5 files changed, 294 insertions(+), 36 deletions(-) > Ping and more comments on this: - is it meaningful to have a limit on the internal queue size (for memory load for instance), or it's not relevant in case of hw accel? - isn't it missing a flush mechanism so seeking can be accurate? [...] -- Clément B. signature.asc Description: PGP signature ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Re: [FFmpeg-devel] [PATCH] videotoolbox: allow to enable the async decoding.
On Sun, Aug 09, 2015 at 01:19:04PM +0200, Hendrik Leppkes wrote: [...] Isnt there a way to put a handle to the async frame into the AVFrame somehow, which can then be used by the user code to get it from VT? Yes that would be great; the special AVVideotoolboxAsyncFrame is kind of annoying to deal with if you're relying on the AVFrame ref counting all around in your app. I would like to see av_videotoolbox_pop_async_frame poping an AVFrame (or NULL if nothing available yet?) with the CVPixelBuffer in data[3] (just like with sync mode), assuming that's do-able. [...] -- Clément B. pgptme6FMLPFg.pgp Description: PGP signature ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Re: [FFmpeg-devel] [PATCH] videotoolbox: allow to enable the async decoding.
2015-08-13 14:26 GMT+08:00 Clément Bœsch u...@pkh.me: On Thu, Aug 13, 2015 at 01:07:38PM +0800, Zhang Rui wrote: status = VTDecompressionSessionDecodeFrame(videotoolbox-session, sample_buf, - 0, // decodeFlags + decodeFlags, NULL,// sourceFrameRefCon 0); // infoFlagsOut Better keep tracking how many decoding samples, that are passed in and not yet received in callback. VideoToolbox doesn't limit the number of frames in callback thread in async mode. doc used to say that VTDecompressionSessionDecodeFrame will block when the internal queue is full in async mode. I haven't check if this is verified in practice though. I was wrong. After a real test, I found out that VTDecompressionSessionDecodeFrame does block when there are several frames in VideoToolbox's internal queue. But when I enter background by pressing home button while async-decoding. VTB hangs for a while and returns kVTVideoDecoderMalfunctionErr. So I have to track the number of frames in VTB's queue, and limit it to 3 frames as a workaround in my own player. I guess this problem may related to the app state. But neither doc nor demo says anything about it. ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Re: [FFmpeg-devel] [PATCH] videotoolbox: allow to enable the async decoding.
On Thu, Aug 13, 2015 at 01:07:38PM +0800, Zhang Rui wrote: status = VTDecompressionSessionDecodeFrame(videotoolbox-session, sample_buf, - 0, // decodeFlags + decodeFlags, NULL,// sourceFrameRefCon 0); // infoFlagsOut Better keep tracking how many decoding samples, that are passed in and not yet received in callback. VideoToolbox doesn't limit the number of frames in callback thread in async mode. doc used to say that VTDecompressionSessionDecodeFrame will block when the internal queue is full in async mode. I haven't check if this is verified in practice though. [...] -- Clément B. pgp67h8ADcit_.pgp Description: PGP signature ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Re: [FFmpeg-devel] [PATCH] videotoolbox: allow to enable the async decoding.
status = VTDecompressionSessionDecodeFrame(videotoolbox-session, sample_buf, - 0, // decodeFlags + decodeFlags, NULL,// sourceFrameRefCon 0); // infoFlagsOut Better keep tracking how many decoding samples, that are passed in and not yet received in callback. VideoToolbox doesn't limit the number of frames in callback thread in async mode. It could be out of memory soon on iPhone without limit, and sooner when application is in background. @@ -586,6 +686,15 @@ static void videotoolbox_default_free(AVCodecContext *avctx) if (videotoolbox-cm_fmt_desc) CFRelease(videotoolbox-cm_fmt_desc); +if (videotoolbox-useAsyncDecoding) { + VTDecompressionSessionWaitForAsynchronousFrames(videotoolbox-session); I doubt if VTDecompressionSessionWaitForAsynchronousFrames guarantee there are no more callback. Doc only says all samples are emitted. Better wait all samples received in callback, or add some check in callback to disable further access to context. + +videotoolbox_clear_queue(videotoolbox); + +if (videotoolbox-queue_mutex != NULL) +videotoolbox_lock_operation(videotoolbox-queue_mutex, AV_LOCK_DESTROY); +} + if (videotoolbox-session) VTDecompressionSessionInvalidate(videotoolbox-session); } ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Re: [FFmpeg-devel] [PATCH] videotoolbox: allow to enable the async decoding.
2015-08-09 19:19 GMT+08:00 Hendrik Leppkes h.lepp...@gmail.com: On Sun, Aug 9, 2015 at 1:11 PM, Sebastien Zwickert dilar...@gmail.com wrote: This patch allows to use the Videotoolbox API in asynchonous mode. Note that when using async decoding the user is responsible for releasing the async frame. Moreover, an option called videotoolbox_async was added to enable async decoding with ffmpeg CLI. I'm not sure about your approach. I can already see you getting loads of problems with frame order. pts is not guaranteed to be present or reliable, and using it for re-ordering your async frames is probably unreliable at best. avcodec does proper re-ordering, and the only reliable way would be to somehow manage to associate the AVFrame avcodec is going to output with your async frame, so that you can trust avcodecs re-ordering. There is a trick used in VLC and XBMC. if (newFrame-pts != AV_NOPTS_VALUE) { newFrame-sort = newFrame-pts; } else { newFrame-sort = newFrame-dts; } avcodec API is better if any exists Isnt there a way to put a handle to the async frame into the AVFrame somehow, which can then be used by the user code to get it from VT? The fourth parameter ''sourceFrameRefCon of VTDecompressionSessionDecodeFrame is passed to callback. ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] videotoolbox: allow to enable the async decoding.
This patch allows to use the Videotoolbox API in asynchonous mode. Note that when using async decoding the user is responsible for releasing the async frame. Moreover, an option called videotoolbox_async was added to enable async decoding with ffmpeg CLI. --- ffmpeg.h | 1 + ffmpeg_opt.c | 1 + ffmpeg_videotoolbox.c | 69 + libavcodec/videotoolbox.c | 186 -- libavcodec/videotoolbox.h | 73 ++ 5 files changed, 294 insertions(+), 36 deletions(-) diff --git a/ffmpeg.h b/ffmpeg.h index 6544e6f..73a1031 100644 --- a/ffmpeg.h +++ b/ffmpeg.h @@ -522,6 +522,7 @@ extern AVIOContext *progress_avio; extern float max_error_rate; extern int vdpau_api_ver; extern char *videotoolbox_pixfmt; +extern int videotoolbox_async; extern const AVIOInterruptCB int_cb; diff --git a/ffmpeg_opt.c b/ffmpeg_opt.c index 28d3051..91be9b9 100644 --- a/ffmpeg_opt.c +++ b/ffmpeg_opt.c @@ -3238,6 +3238,7 @@ const OptionDef options[] = { #endif #if CONFIG_VDA || CONFIG_VIDEOTOOLBOX { videotoolbox_pixfmt, HAS_ARG | OPT_STRING | OPT_EXPERT, { videotoolbox_pixfmt}, }, +{ videotoolbox_async, HAS_ARG | OPT_INT | OPT_EXPERT, { videotoolbox_async}, }, #endif { autorotate, HAS_ARG | OPT_BOOL | OPT_SPEC | OPT_EXPERT | OPT_INPUT, { .off = OFFSET(autorotate) }, diff --git a/ffmpeg_videotoolbox.c b/ffmpeg_videotoolbox.c index 6688452..0bb0600 100644 --- a/ffmpeg_videotoolbox.c +++ b/ffmpeg_videotoolbox.c @@ -34,21 +34,42 @@ typedef struct VTContext { } VTContext; char *videotoolbox_pixfmt; +int videotoolbox_async; static int videotoolbox_retrieve_data(AVCodecContext *s, AVFrame *frame) { InputStream *ist = s-opaque; VTContext *vt = ist-hwaccel_ctx; -CVPixelBufferRef pixbuf = (CVPixelBufferRef)frame-data[3]; -OSType pixel_format = CVPixelBufferGetPixelFormatType(pixbuf); +AVVideotoolboxContext *videotoolbox = s-hwaccel_context; +AVVideotoolboxAsyncFrame *async_frame = NULL; +CVPixelBufferRef pixbuf; +OSType pixel_format; CVReturn err; uint8_t *data[4] = { 0 }; int linesize[4] = { 0 }; int planes, ret, i; char codec_str[32]; +int width, height; av_frame_unref(vt-tmp_frame); +if (videotoolbox-useAsyncDecoding) { +async_frame = av_videotoolbox_pop_async_frame(videotoolbox); + +if (!async_frame) +return -1; + +pixbuf = async_frame-cv_buffer; +width = CVPixelBufferGetWidth(pixbuf); +height = CVPixelBufferGetHeight(pixbuf); +} else { +pixbuf = (CVPixelBufferRef)frame-data[3]; +width = frame-width; +height = frame-height; +} + +pixel_format = CVPixelBufferGetPixelFormatType(pixbuf); + switch (pixel_format) { case kCVPixelFormatType_420YpCbCr8Planar: vt-tmp_frame-format = AV_PIX_FMT_YUV420P; break; case kCVPixelFormatType_422YpCbCr8: vt-tmp_frame-format = AV_PIX_FMT_UYVY422; break; @@ -60,19 +81,21 @@ static int videotoolbox_retrieve_data(AVCodecContext *s, AVFrame *frame) av_get_codec_tag_string(codec_str, sizeof(codec_str), s-codec_tag); av_log(NULL, AV_LOG_ERROR, %s: Unsupported pixel format: %s\n, codec_str, videotoolbox_pixfmt); -return AVERROR(ENOSYS); +ret = AVERROR(ENOSYS); +goto fail; } -vt-tmp_frame-width = frame-width; -vt-tmp_frame-height = frame-height; +vt-tmp_frame-width = width; +vt-tmp_frame-height = height; ret = av_frame_get_buffer(vt-tmp_frame, 32); -if (ret 0) -return ret; - +if (ret 0) { +goto fail; +} err = CVPixelBufferLockBaseAddress(pixbuf, kCVPixelBufferLock_ReadOnly); if (err != kCVReturnSuccess) { av_log(NULL, AV_LOG_ERROR, Error locking the pixel buffer.\n); -return AVERROR_UNKNOWN; +ret = AVERROR_UNKNOWN; +goto fail; } if (CVPixelBufferIsPlanar(pixbuf)) { @@ -89,17 +112,27 @@ static int videotoolbox_retrieve_data(AVCodecContext *s, AVFrame *frame) av_image_copy(vt-tmp_frame-data, vt-tmp_frame-linesize, (const uint8_t **)data, linesize, vt-tmp_frame-format, - frame-width, frame-height); + width, height); ret = av_frame_copy_props(vt-tmp_frame, frame); CVPixelBufferUnlockBaseAddress(pixbuf, kCVPixelBufferLock_ReadOnly); -if (ret 0) -return ret; +if (ret 0) { +goto fail; +} av_frame_unref(frame); av_frame_move_ref(frame, vt-tmp_frame); +if (videotoolbox-useAsyncDecoding) { +av_videotoolbox_release_async_frame(async_frame); +} + return 0; +fail: +if (videotoolbox-useAsyncDecoding) { +av_videotoolbox_release_async_frame(async_frame); +} +return ret; } static void videotoolbox_uninit(AVCodecContext *s)
Re: [FFmpeg-devel] [PATCH] videotoolbox: allow to enable the async decoding.
On Sun, 9 Aug 2015 07:46:53 -0400 Ronald S. Bultje rsbul...@gmail.com wrote: Hi, On Sun, Aug 9, 2015 at 7:11 AM, Sebastien Zwickert dilar...@gmail.com wrote: This patch allows to use the Videotoolbox API in asynchonous mode. If this is for N:M (in:out), I think this should be replaced by a prospective avcodec async decoding API. Definitely. ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Re: [FFmpeg-devel] [PATCH] videotoolbox: allow to enable the async decoding.
On Sun, Aug 9, 2015 at 1:11 PM, Sebastien Zwickert dilar...@gmail.com wrote: This patch allows to use the Videotoolbox API in asynchonous mode. Note that when using async decoding the user is responsible for releasing the async frame. Moreover, an option called videotoolbox_async was added to enable async decoding with ffmpeg CLI. I'm not sure about your approach. I can already see you getting loads of problems with frame order. pts is not guaranteed to be present or reliable, and using it for re-ordering your async frames is probably unreliable at best. avcodec does proper re-ordering, and the only reliable way would be to somehow manage to associate the AVFrame avcodec is going to output with your async frame, so that you can trust avcodecs re-ordering. Isnt there a way to put a handle to the async frame into the AVFrame somehow, which can then be used by the user code to get it from VT? - Hendrik ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel