From 531340a145b575eb06172f3e09a6645ac6ee2b31 Mon Sep 17 00:00:00 2001 From: nick black Date: Sun, 1 Dec 2019 12:43:20 -0500 Subject: [PATCH] libav: return averr to caller #86 --- include/notcurses.h | 13 ++++-- src/lib/libav.c | 83 ++++++++++++++++----------------- src/view/{main.cpp => view.cpp} | 30 ++++++++---- tests/libav.cpp | 15 ++++-- 4 files changed, 81 insertions(+), 60 deletions(-) rename src/view/{main.cpp => view.cpp} (56%) diff --git a/include/notcurses.h b/include/notcurses.h index c0776f9f5..74c7607b4 100644 --- a/include/notcurses.h +++ b/include/notcurses.h @@ -589,16 +589,19 @@ ncplane_double_box_cells(struct ncplane* n, cell* ul, cell* ur, cell* ll, struct AVFrame; // open a visual (image or video), associating it with the specified ncplane. -API struct ncvisual* ncplane_visual_open(struct ncplane* nc, const char* file); +// returns NULL on any error, writing the AVError to 'averr'. +API struct ncvisual* ncplane_visual_open(struct ncplane* nc, const char* file, + int* averr); // destroy an ncvisual. rendered elements will not be disrupted, but the visual // can be neither decoded nor rendered any further. API void ncvisual_destroy(struct ncvisual* ncv); -// extract the next frame from an ncvisual. returns NULL on a decoding or -// allocation error. if all frames have been returned, starts over at the -// beginning. for a still image, all calls will return the same frame. -API struct AVFrame* ncvisual_decode(struct ncvisual* nc); +// extract the next frame from an ncvisual. returns NULL on end of file, writing +// 0 to 'averr'. returns NULL on a decoding or allocation error, placing the +// AVError in 'averr'. this frame is invalidated by a subsequent call to +// ncvisual_decode(), and should not be freed by the caller. +API struct AVFrame* ncvisual_decode(struct ncvisual* nc, int* averr); // render the next frame to the associated ncplane at the current cursor // position, going through ystop/xstop. the frame will be scaled to the diff --git a/src/lib/libav.c b/src/lib/libav.c index fa21d7a75..871e53100 100644 --- a/src/lib/libav.c +++ b/src/lib/libav.c @@ -77,33 +77,25 @@ print_frame_summary(const AVCodecContext* cctx, const AVFrame* f){ f->quality); } -AVFrame* ncvisual_decode(struct ncvisual* nc){ - int ret; +AVFrame* ncvisual_decode(struct ncvisual* nc, int* averr){ +fprintf(stderr, "\n*********************running decode+scale\n"); if(nc->packet_outstanding){ - ret = avcodec_send_packet(nc->codecctx, nc->packet); - if(ret < 0){ - fprintf(stderr, "Error processing AVPacket (%s)\n", av_err2str(ret)); + *averr = avcodec_send_packet(nc->codecctx, nc->packet); + if(*averr < 0){ + fprintf(stderr, "Error processing AVPacket (%s)\n", av_err2str(*averr)); return NULL; } --nc->packet_outstanding; } - ret = avcodec_receive_frame(nc->codecctx, nc->frame); - if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){ + *averr = avcodec_receive_frame(nc->codecctx, nc->frame); + if(*averr == AVERROR(EAGAIN) || *averr == AVERROR_EOF){ return NULL; // FIXME do something smarter - }else if(ret < 0){ - fprintf(stderr, "Error decoding AVPacket (%s)\n", av_err2str(ret)); + }else if(*averr < 0){ + fprintf(stderr, "Error decoding AVPacket (%s)\n", av_err2str(*averr)); return NULL; } print_frame_summary(nc->codecctx, nc->frame); - return nc->frame; #define IMGALLOCALIGN 32 - /* - ret = av_image_alloc(nc->frame->data, nc->frame->linesize, nc->frame->width, - nc->frame->height, nc->frame->format, IMGALLOCALIGN); - if(ret < 0){ - fprintf(stderr, "Error allocating input data (%s)\n", av_err2str(ret)); - return NULL; - } nc->swsctx = sws_getCachedContext(nc->swsctx, nc->frame->width, nc->frame->height, @@ -114,7 +106,7 @@ print_frame_summary(nc->codecctx, nc->frame); SWS_LANCZOS, NULL, NULL, NULL); if(nc->swsctx == NULL){ - fprintf(stderr, "Error retrieving swsctx (%s)\n", av_err2str(ret)); + fprintf(stderr, "Error retrieving swsctx (%s)\n", av_err2str(*averr)); return NULL; } AVFrame* oframe = av_frame_alloc(); @@ -125,53 +117,57 @@ print_frame_summary(nc->codecctx, nc->frame); oframe->format = AV_PIX_FMT_RGB24; oframe->width = nc->dstwidth; oframe->height = nc->dstheight; - if((ret = av_image_alloc(oframe->data, oframe->linesize, oframe->width, oframe->height, - oframe->format, IMGALLOCALIGN)) < 0){ - fprintf(stderr, "Error allocating visual data (%s)\n", av_err2str(ret)); + if((*averr = av_image_alloc(oframe->data, oframe->linesize, oframe->width, oframe->height, + oframe->format, IMGALLOCALIGN)) < 0){ + fprintf(stderr, "Error allocating visual data (%s)\n", av_err2str(*averr)); av_frame_free(&oframe); return NULL; } - ret = sws_scale(nc->swsctx, (const uint8_t* const*)nc->frame->data, nc->frame->linesize, 0, - nc->frame->height, oframe->data, oframe->linesize); - if(ret < 0){ - fprintf(stderr, "Error applying scaling (%s)\n", av_err2str(ret)); +print_frame_summary(nc->codecctx, oframe); + *averr = sws_scale(nc->swsctx, (const uint8_t* const*)nc->frame->data, nc->frame->linesize, 0, + nc->frame->height, oframe->data, oframe->linesize); + if(*averr < 0){ + fprintf(stderr, "Error applying scaling (%s)\n", av_err2str(*averr)); av_frame_free(&oframe); return NULL; } +print_frame_summary(nc->codecctx, oframe); #undef IMGALLOCALIGN return oframe; - */ } -ncvisual* ncplane_visual_open(struct ncplane* nc, const char* filename){ +ncvisual* ncplane_visual_open(struct ncplane* nc, const char* filename, int* averr){ ncvisual* ncv = ncvisual_create(); if(ncv == NULL){ fprintf(stderr, "Couldn't create %s (%s)\n", filename, strerror(errno)); + *averr = AVERROR(ENOMEM); return NULL; } + memset(ncv, 0, sizeof(*ncv)); ncplane_dim_yx(nc, &ncv->dstheight, &ncv->dstwidth); - int ret = avformat_open_input(&ncv->fmtctx, filename, NULL, NULL); - if(ret < 0){ - fprintf(stderr, "Couldn't open %s (%s)\n", filename, av_err2str(ret)); + *averr = avformat_open_input(&ncv->fmtctx, filename, NULL, NULL); + if(*averr < 0){ + fprintf(stderr, "Couldn't open %s (%s)\n", filename, av_err2str(*averr)); goto err; } - if((ret = avformat_find_stream_info(ncv->fmtctx, NULL)) < 0){ + if((*averr = avformat_find_stream_info(ncv->fmtctx, NULL)) < 0){ fprintf(stderr, "Error extracting stream info from %s (%s)\n", filename, - av_err2str(ret)); + av_err2str(*averr)); goto err; } // av_dump_format(ncv->fmtctx, 0, filename, false); if((ncv->packet = av_packet_alloc()) == NULL){ fprintf(stderr, "Couldn't allocate packet for %s\n", filename); + *averr = AVERROR(ENOMEM); goto err; } - if((ret = av_read_frame(ncv->fmtctx, ncv->packet)) < 0){ + if((*averr = av_read_frame(ncv->fmtctx, ncv->packet)) < 0){ fprintf(stderr, "Error reading frame info from %s (%s)\n", filename, - av_err2str(ret)); + av_err2str(*averr)); goto err; } - if((ret = av_find_best_stream(ncv->fmtctx, AVMEDIA_TYPE_VIDEO, -1, -1, &ncv->codec, 0)) < 0){ - fprintf(stderr, "Couldn't find visuals in %s (%s)\n", filename, av_err2str(ret)); + if((*averr = av_find_best_stream(ncv->fmtctx, AVMEDIA_TYPE_VIDEO, -1, -1, &ncv->codec, 0)) < 0){ + fprintf(stderr, "Couldn't find visuals in %s (%s)\n", filename, av_err2str(*averr)); goto err; } if(ncv->codec == NULL){ @@ -180,28 +176,31 @@ ncvisual* ncplane_visual_open(struct ncplane* nc, const char* filename){ } if((ncv->codecctx = avcodec_alloc_context3(ncv->codec)) == NULL){ fprintf(stderr, "Couldn't allocate decoder for %s\n", filename); + *averr = AVERROR(ENOMEM); goto err; } - if((ret = avcodec_open2(ncv->codecctx, ncv->codec, NULL)) < 0){ - fprintf(stderr, "Couldn't open codec for %s (%s)\n", filename, av_err2str(ret)); + if((*averr = avcodec_open2(ncv->codecctx, ncv->codec, NULL)) < 0){ + fprintf(stderr, "Couldn't open codec for %s (%s)\n", filename, av_err2str(*averr)); goto err; } if((ncv->cparams = avcodec_parameters_alloc()) == NULL){ fprintf(stderr, "Couldn't allocate codec params for %s\n", filename); + *averr = AVERROR(ENOMEM); goto err; } - if((ret = avcodec_parameters_from_context(ncv->cparams, ncv->codecctx)) < 0){ - fprintf(stderr, "Couldn't get codec params for %s (%s)\n", filename, av_err2str(ret)); + if((*averr = avcodec_parameters_from_context(ncv->cparams, ncv->codecctx)) < 0){ + fprintf(stderr, "Couldn't get codec params for %s (%s)\n", filename, av_err2str(*averr)); goto err; } - if((ret = avcodec_send_packet(ncv->codecctx, ncv->packet)) < 0){ + if((*averr = avcodec_send_packet(ncv->codecctx, ncv->packet)) < 0){ fprintf(stderr, "Error decoding packet from %s (%s)\n", filename, - av_err2str(ret)); + av_err2str(*averr)); goto err; } ++ncv->packet_outstanding; if((ncv->frame = av_frame_alloc()) == NULL){ fprintf(stderr, "Couldn't allocate frame for %s\n", filename); + *averr = AVERROR(ENOMEM); goto err; } return ncv; diff --git a/src/view/main.cpp b/src/view/view.cpp similarity index 56% rename from src/view/main.cpp rename to src/view/view.cpp index fcc7e7420..218b67579 100644 --- a/src/view/main.cpp +++ b/src/view/view.cpp @@ -1,9 +1,14 @@ +#include #include #include #include + +extern "C" { #include #include #include +} + #include "notcurses.h" static void usage(std::ostream& os, const char* name, int exitcode) @@ -14,15 +19,16 @@ void usage(std::ostream& o, const char* name, int exitcode){ exit(exitcode); } -int ncview(struct ncvisual* ncv){ +int ncview(struct notcurses* nc, struct ncvisual* ncv, int* averr){ + struct ncplane* n = notcurses_stdplane(nc); + int frame = 0; AVFrame* avf; - if((avf = ncvisual_decode(ncv)) == nullptr){ - return -1; + while( (avf = ncvisual_decode(ncv, averr)) ){ + ncplane_cursor_move_yx(n, 0, 0); + ncplane_printf(n, "Got frame %05d\u2026", frame); + ++frame; } - printf("%s: %dx%d aspect %d:%d %d\n", avf->key_frame ? "Keyframe" : "Frame", - avf->height, avf->width, avf->sample_aspect_ratio.num, - avf->sample_aspect_ratio.den, avf->format); - return 0; + return *averr; } int main(int argc, char** argv){ @@ -38,12 +44,18 @@ int main(int argc, char** argv){ } auto ncp = notcurses_stdplane(nc); for(int i = 1 ; i < argc ; ++i){ - auto ncv = ncplane_visual_open(ncp, argv[i]); + std::array errbuf; + int averr; + auto ncv = ncplane_visual_open(ncp, argv[i], &averr); if(ncv == nullptr){ + av_make_error_string(errbuf.data(), errbuf.size(), averr); + std::cerr << "Error opening " << argv[i] << ": " << errbuf.data() << std::endl; success = false; continue; } - if(ncview(ncv)){ + if(ncview(nc, ncv, &averr)){ + av_make_error_string(errbuf.data(), errbuf.size(), averr); + std::cerr << "Error decoding " << argv[i] << ": " << errbuf.data() << std::endl; success = false; } ncvisual_destroy(ncv); diff --git a/tests/libav.cpp b/tests/libav.cpp index 1224a7289..a64553390 100644 --- a/tests/libav.cpp +++ b/tests/libav.cpp @@ -30,15 +30,22 @@ class LibavTest : public :: testing::Test { }; TEST_F(LibavTest, LoadImage) { - auto ncv = ncplane_visual_open(ncp_, "../tests/dsscaw-purp.png"); + int averr; + auto ncv = ncplane_visual_open(ncp_, "../tests/dsscaw-purp.png", &averr); + EXPECT_EQ(0, averr); ASSERT_NE(nullptr, ncv); - ASSERT_NE(nullptr, ncvisual_decode(ncv)); + ASSERT_NE(nullptr, ncvisual_decode(ncv, &averr)); + EXPECT_EQ(0, averr); ncvisual_destroy(ncv); } +// FIXME ought run through full video, not just first frame TEST_F(LibavTest, LoadVideo) { - auto ncv = ncplane_visual_open(ncp_, "../tests/atliens.mkv"); + int averr; + auto ncv = ncplane_visual_open(ncp_, "../tests/atliens.mkv", &averr); + EXPECT_EQ(0, averr); ASSERT_NE(nullptr, ncv); - ASSERT_NE(nullptr, ncvisual_decode(ncv)); + ASSERT_NE(nullptr, ncvisual_decode(ncv, &averr)); + EXPECT_EQ(0, averr); ncvisual_destroy(ncv); }