mirror of
https://github.com/webmproject/libwebp.git
synced 2025-09-01 08:42:19 +02:00
Implement progress report (and user abort)
New cwebp flag is -progress Change-Id: Ied872cca13f512036860783bbee1bdbccad72768
This commit is contained in:
committed by
James Zern
parent
eda520a92e
commit
30971c9e95
@@ -46,7 +46,7 @@ int VP8EncFinishAlpha(VP8Encoder* enc) {
|
||||
enc->alpha_data_size_ = (uint32_t)tmp_size;
|
||||
enc->alpha_data_ = tmp_data;
|
||||
}
|
||||
return 1;
|
||||
return WebPReportProgress(enc, enc->percent_ + 20);
|
||||
}
|
||||
|
||||
void VP8EncDeleteAlpha(VP8Encoder* enc) {
|
||||
|
@@ -339,6 +339,7 @@ static void MBAnalyze(VP8EncIterator* const it,
|
||||
// this stage.
|
||||
|
||||
int VP8EncAnalyze(VP8Encoder* const enc) {
|
||||
int ok = 1;
|
||||
int alphas[256] = { 0 };
|
||||
VP8EncIterator it;
|
||||
|
||||
@@ -347,12 +348,13 @@ int VP8EncAnalyze(VP8Encoder* const enc) {
|
||||
do {
|
||||
VP8IteratorImport(&it);
|
||||
MBAnalyze(&it, alphas, &enc->uv_alpha_);
|
||||
ok = VP8IteratorProgress(&it, 20);
|
||||
// Let's pretend we have perfect lossless reconstruction.
|
||||
} while (VP8IteratorNext(&it, it.yuv_in_));
|
||||
} while (ok && VP8IteratorNext(&it, it.yuv_in_));
|
||||
enc->uv_alpha_ /= enc->mb_w_ * enc->mb_h_;
|
||||
AssignSegments(enc, alphas);
|
||||
if (ok) AssignSegments(enc, alphas);
|
||||
|
||||
return 1;
|
||||
return ok;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
|
@@ -560,6 +560,7 @@ static void ResetAfterSkip(VP8EncIterator* const it) {
|
||||
|
||||
int VP8EncLoop(VP8Encoder* const enc) {
|
||||
int i, s, p;
|
||||
int ok = 1;
|
||||
VP8EncIterator it;
|
||||
VP8ModeScore info;
|
||||
const int dont_use_skip = !enc->proba_.use_skip_proba_;
|
||||
@@ -595,12 +596,17 @@ int VP8EncLoop(VP8Encoder* const enc) {
|
||||
StoreSideInfo(&it);
|
||||
VP8StoreFilterStats(&it);
|
||||
VP8IteratorExport(&it);
|
||||
} while (VP8IteratorNext(&it, it.yuv_out_));
|
||||
VP8AdjustFilterStrength(&it);
|
||||
ok = VP8IteratorProgress(&it, 20);
|
||||
} while (ok && VP8IteratorNext(&it, it.yuv_out_));
|
||||
|
||||
if (ok) {
|
||||
VP8AdjustFilterStrength(&it);
|
||||
}
|
||||
|
||||
// Finalize the partitions
|
||||
for (p = 0; p < enc->num_parts_; ++p) {
|
||||
VP8BitWriterFinish(enc->parts_ + p);
|
||||
ok &= !enc->parts_[p].error_;
|
||||
}
|
||||
// and byte counters
|
||||
if (enc->pic_->stats) {
|
||||
@@ -610,7 +616,10 @@ int VP8EncLoop(VP8Encoder* const enc) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
if (!ok) { // need to do some memory cleanup
|
||||
VP8EncFreeBitWriters(enc);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -622,7 +631,7 @@ int VP8EncLoop(VP8Encoder* const enc) {
|
||||
#define kHeaderSizeEstimate (15 + 20 + 10) // TODO: fix better
|
||||
|
||||
static int OneStatPass(VP8Encoder* const enc, float q, int rd_opt, int nb_mbs,
|
||||
float* const PSNR) {
|
||||
float* const PSNR, int percent_delta) {
|
||||
VP8EncIterator it;
|
||||
uint64_t size = 0;
|
||||
uint64_t distortion = 0;
|
||||
@@ -651,6 +660,8 @@ static int OneStatPass(VP8Encoder* const enc, float q, int rd_opt, int nb_mbs,
|
||||
RecordResiduals(&it, &info);
|
||||
size += info.R;
|
||||
distortion += info.D;
|
||||
if (percent_delta && !VP8IteratorProgress(&it, percent_delta))
|
||||
return 0;
|
||||
} while (VP8IteratorNext(&it, it.yuv_out_) && --nb_mbs > 0);
|
||||
size += FinalizeSkipProba(enc);
|
||||
size += FinalizeTokenProbas(enc);
|
||||
@@ -671,6 +682,10 @@ int VP8StatLoop(VP8Encoder* const enc) {
|
||||
(enc->config_->target_size > 0 || enc->config_->target_PSNR > 0);
|
||||
const int fast_probe = (enc->method_ < 2 && !do_search);
|
||||
float q = enc->config_->quality;
|
||||
const int max_passes = enc->config_->pass;
|
||||
const int task_percent = 20;
|
||||
const int percent_per_pass = (task_percent + max_passes / 2) / max_passes;
|
||||
const int final_percent = enc->percent_ + task_percent;
|
||||
int pass;
|
||||
int nb_mbs;
|
||||
|
||||
@@ -680,36 +695,38 @@ int VP8StatLoop(VP8Encoder* const enc) {
|
||||
|
||||
// No target size: just do several pass without changing 'q'
|
||||
if (!do_search) {
|
||||
for (pass = 0; pass < enc->config_->pass; ++pass) {
|
||||
for (pass = 0; pass < max_passes; ++pass) {
|
||||
const int rd_opt = (enc->method_ > 2);
|
||||
OneStatPass(enc, q, rd_opt, nb_mbs, NULL);
|
||||
if (!OneStatPass(enc, q, rd_opt, nb_mbs, NULL, percent_per_pass)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// binary search for a size close to target
|
||||
for (pass = 0; pass < enc->config_->pass && (dqs[pass] > 0); ++pass) {
|
||||
const int rd_opt = 1;
|
||||
float PSNR;
|
||||
int criterion;
|
||||
const int size = OneStatPass(enc, q, rd_opt, nb_mbs, &PSNR);
|
||||
} else {
|
||||
// binary search for a size close to target
|
||||
for (pass = 0; pass < max_passes && (dqs[pass] > 0); ++pass) {
|
||||
const int rd_opt = 1;
|
||||
float PSNR;
|
||||
int criterion;
|
||||
const int size = OneStatPass(enc, q, rd_opt, nb_mbs, &PSNR,
|
||||
percent_per_pass);
|
||||
#if DEBUG_SEARCH
|
||||
printf("#%d size=%d PSNR=%.2f q=%.2f\n", pass, size, PSNR, q);
|
||||
printf("#%d size=%d PSNR=%.2f q=%.2f\n", pass, size, PSNR, q);
|
||||
#endif
|
||||
|
||||
if (enc->config_->target_PSNR > 0) {
|
||||
criterion = (PSNR < enc->config_->target_PSNR);
|
||||
} else {
|
||||
criterion = (size < enc->config_->target_size);
|
||||
}
|
||||
// dichotomize
|
||||
if (criterion) {
|
||||
q += dqs[pass];
|
||||
} else {
|
||||
q -= dqs[pass];
|
||||
if (!size) return 0;
|
||||
if (enc->config_->target_PSNR > 0) {
|
||||
criterion = (PSNR < enc->config_->target_PSNR);
|
||||
} else {
|
||||
criterion = (size < enc->config_->target_size);
|
||||
}
|
||||
// dichotomize
|
||||
if (criterion) {
|
||||
q += dqs[pass];
|
||||
} else {
|
||||
q -= dqs[pass];
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
return WebPReportProgress(enc, final_percent);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
@@ -66,9 +66,18 @@ void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it) {
|
||||
it->yuv_out2_ = enc->yuv_out2_;
|
||||
it->yuv_p_ = enc->yuv_p_;
|
||||
it->lf_stats_ = enc->lf_stats_;
|
||||
it->percent0_ = enc->percent_;
|
||||
VP8IteratorReset(it);
|
||||
}
|
||||
|
||||
int VP8IteratorProgress(const VP8EncIterator* const it, int delta) {
|
||||
if (delta && it->enc_->pic_->progress_hook) {
|
||||
const int percent = it->percent0_ + delta * it->y_ / (it->enc_->mb_h_ - 1);
|
||||
return WebPReportProgress(it->enc_, percent);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Import the source samples into the cache. Takes care of replicating
|
||||
// boundary pixels if necessary.
|
||||
|
@@ -18,10 +18,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
|
||||
#endif /* WEBP_EXPERIMENTAL_FEATURES */
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void VP8EncInitLayer(VP8Encoder* const enc) {
|
||||
@@ -35,8 +31,6 @@ void VP8EncInitLayer(VP8Encoder* const enc) {
|
||||
|
||||
void VP8EncCodeLayerBlock(VP8EncIterator* it) {
|
||||
(void)it; // remove a warning
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
#endif /* WEBP_EXPERIMENTAL_FEATURES */
|
||||
}
|
||||
|
||||
int VP8EncFinishLayer(VP8Encoder* const enc) {
|
||||
|
@@ -356,9 +356,20 @@ static size_t GeneratePartition0(VP8Encoder* const enc) {
|
||||
return !bw->error_;
|
||||
}
|
||||
|
||||
void VP8EncFreeBitWriters(VP8Encoder* const enc) {
|
||||
int p;
|
||||
VP8BitWriterWipeOut(&enc->bw_);
|
||||
for (p = 0; p < enc->num_parts_; ++p) {
|
||||
VP8BitWriterWipeOut(enc->parts_ + p);
|
||||
}
|
||||
}
|
||||
|
||||
int VP8EncWrite(VP8Encoder* const enc) {
|
||||
WebPPicture* const pic = enc->pic_;
|
||||
VP8BitWriter* const bw = &enc->bw_;
|
||||
const int task_percent = 19;
|
||||
const int percent_per_part = task_percent / enc->num_parts_;
|
||||
const int final_percent = enc->percent_ + task_percent;
|
||||
int ok = 0;
|
||||
size_t vp8_size, pad, riff_size;
|
||||
int p;
|
||||
@@ -399,7 +410,7 @@ int VP8EncWrite(VP8Encoder* const enc) {
|
||||
ok = ok && PutWebPHeaders(enc, size0, vp8_size, riff_size)
|
||||
&& pic->writer(part0, size0, pic)
|
||||
&& EmitPartitionsSize(enc, pic);
|
||||
free((void*)part0);
|
||||
VP8BitWriterWipeOut(bw); // will free the internal buffer.
|
||||
}
|
||||
|
||||
// Token partitions
|
||||
@@ -408,7 +419,8 @@ int VP8EncWrite(VP8Encoder* const enc) {
|
||||
const size_t size = VP8BitWriterSize(enc->parts_ + p);
|
||||
if (size)
|
||||
ok = ok && pic->writer(buf, size, pic);
|
||||
free((void*)buf);
|
||||
VP8BitWriterWipeOut(enc->parts_ + p); // will free the internal buffer.
|
||||
ok = ok && WebPReportProgress(enc, enc->percent_ + percent_per_part);
|
||||
}
|
||||
|
||||
// Padding byte
|
||||
@@ -417,6 +429,7 @@ int VP8EncWrite(VP8Encoder* const enc) {
|
||||
}
|
||||
|
||||
enc->coded_size_ = (int)(CHUNK_HEADER_SIZE + riff_size);
|
||||
ok = ok && WebPReportProgress(enc, final_percent);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
@@ -272,6 +272,7 @@ typedef struct {
|
||||
LFStats* lf_stats_; // filter stats (borrowed from enc_)
|
||||
int do_trellis_; // if true, perform extra level optimisation
|
||||
int done_; // true when scan is finished
|
||||
int percent0_; // saved initial progress percent
|
||||
} VP8EncIterator;
|
||||
|
||||
// in iterator.c
|
||||
@@ -288,6 +289,9 @@ void VP8IteratorExport(const VP8EncIterator* const it);
|
||||
// it->yuv_out_ or it->yuv_in_.
|
||||
int VP8IteratorNext(VP8EncIterator* const it,
|
||||
const uint8_t* const block_to_save);
|
||||
// Report progression based on macroblock rows. Return 0 for user-abort request.
|
||||
int VP8IteratorProgress(const VP8EncIterator* const it,
|
||||
int final_delta_percent);
|
||||
// Intra4x4 iterations
|
||||
void VP8IteratorStartI4(VP8EncIterator* const it);
|
||||
// returns true if not done.
|
||||
@@ -330,6 +334,8 @@ struct VP8Encoder {
|
||||
VP8BitWriter bw_; // part0
|
||||
VP8BitWriter parts_[MAX_NUM_PARTITIONS]; // token partitions
|
||||
|
||||
int percent_; // for progress
|
||||
|
||||
// transparency blob
|
||||
int has_alpha_;
|
||||
uint8_t* alpha_data_; // non-NULL if transparency is present
|
||||
@@ -401,6 +407,8 @@ void VP8CodeIntraModes(VP8Encoder* const enc);
|
||||
// and appending an assembly of all the pre-coded token partitions.
|
||||
// Return true if everything is ok.
|
||||
int VP8EncWrite(VP8Encoder* const enc);
|
||||
// Release memory allocated for bit-writing in VP8EncLoop & seq.
|
||||
void VP8EncFreeBitWriters(VP8Encoder* const enc);
|
||||
|
||||
// in frame.c
|
||||
extern const uint8_t VP8EncBands[16 + 1];
|
||||
@@ -422,6 +430,7 @@ int VP8StatLoop(VP8Encoder* const enc);
|
||||
// in webpenc.c
|
||||
// Assign an error code to a picture. Return false for convenience.
|
||||
int WebPEncodingSetError(WebPPicture* const pic, WebPEncodingError error);
|
||||
int WebPReportProgress(VP8Encoder* const enc, int percent);
|
||||
|
||||
// in analysis.c
|
||||
// Main analysis loop. Decides the segmentations and complexity.
|
||||
|
@@ -242,6 +242,7 @@ static VP8Encoder* InitEncoder(const WebPConfig* const config,
|
||||
enc->config_ = config;
|
||||
enc->profile_ = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2;
|
||||
enc->pic_ = picture;
|
||||
enc->percent_ = 0;
|
||||
|
||||
MapConfigToTools(enc);
|
||||
VP8EncDspInit();
|
||||
@@ -301,15 +302,28 @@ static void StoreStats(VP8Encoder* const enc) {
|
||||
stats->block_count[i] = enc->block_count_[i];
|
||||
}
|
||||
}
|
||||
WebPReportProgress(enc, 100); // done!
|
||||
}
|
||||
|
||||
int WebPEncodingSetError(WebPPicture* const pic, WebPEncodingError error) {
|
||||
assert((int)error <= VP8_ENC_ERROR_BAD_WRITE);
|
||||
assert((int)error < VP8_ENC_ERROR_LAST);
|
||||
assert((int)error >= VP8_ENC_OK);
|
||||
pic->error_code = error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WebPReportProgress(VP8Encoder* const enc, int percent) {
|
||||
if (percent != enc->percent_) {
|
||||
WebPPicture* const pic = enc->pic_;
|
||||
enc->percent_ = percent;
|
||||
if (pic->progress_hook && !pic->progress_hook(percent, pic)) {
|
||||
// user abort requested
|
||||
WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1; // ok
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) {
|
||||
@@ -332,6 +346,7 @@ int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) {
|
||||
|
||||
enc = InitEncoder(config, pic);
|
||||
if (enc == NULL) return 0; // pic->error is already set.
|
||||
// Note: each of the tasks below account for 20% in the progress report.
|
||||
ok = VP8EncAnalyze(enc)
|
||||
&& VP8StatLoop(enc)
|
||||
&& VP8EncLoop(enc)
|
||||
@@ -341,6 +356,9 @@ int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) {
|
||||
#endif
|
||||
&& VP8EncWrite(enc);
|
||||
StoreStats(enc);
|
||||
if (!ok) {
|
||||
VP8EncFreeBitWriters(enc);
|
||||
}
|
||||
DeleteEncoder(enc);
|
||||
|
||||
return ok;
|
||||
|
Reference in New Issue
Block a user