You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
834 lines
22 KiB
834 lines
22 KiB
2 years ago
|
/*
|
||
|
libde265 example application "dec265".
|
||
|
|
||
|
MIT License
|
||
|
|
||
|
Copyright (c) 2013-2014 struktur AG, Dirk Farin <farin@struktur.de>
|
||
|
|
||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
of this software and associated documentation files (the "Software"), to deal
|
||
|
in the Software without restriction, including without limitation the rights
|
||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
copies of the Software, and to permit persons to whom the Software is
|
||
|
furnished to do so, subject to the following conditions:
|
||
|
|
||
|
The above copyright notice and this permission notice shall be included in all
|
||
|
copies or substantial portions of the Software.
|
||
|
|
||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
|
SOFTWARE.
|
||
|
*/
|
||
|
|
||
|
#define DO_MEMORY_LOGGING 0
|
||
|
|
||
|
#include "de265.h"
|
||
|
#ifdef HAVE_CONFIG_H
|
||
|
#include "config.h"
|
||
|
#endif
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <limits>
|
||
|
#include <getopt.h>
|
||
|
#ifdef HAVE_MALLOC_H
|
||
|
#include <malloc.h>
|
||
|
#endif
|
||
|
#include <signal.h>
|
||
|
|
||
|
#ifndef _MSC_VER
|
||
|
#include <sys/time.h>
|
||
|
#include <unistd.h>
|
||
|
#endif
|
||
|
|
||
|
#include "libde265/quality.h"
|
||
|
|
||
|
#if HAVE_VIDEOGFX
|
||
|
#include <libvideogfx.hh>
|
||
|
using namespace videogfx;
|
||
|
#endif
|
||
|
|
||
|
#if HAVE_SDL
|
||
|
#include "sdl.hh"
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#define BUFFER_SIZE 40960
|
||
|
#define NUM_THREADS 4
|
||
|
|
||
|
int nThreads=0;
|
||
|
bool nal_input=false;
|
||
|
int quiet=0;
|
||
|
bool check_hash=false;
|
||
|
bool show_help=false;
|
||
|
bool dump_headers=false;
|
||
|
bool write_yuv=false;
|
||
|
bool output_with_videogfx=false;
|
||
|
bool logging=true;
|
||
|
bool no_acceleration=false;
|
||
|
const char *output_filename = "out.yuv";
|
||
|
uint32_t max_frames=UINT32_MAX;
|
||
|
bool write_bytestream=false;
|
||
|
const char *bytestream_filename;
|
||
|
bool measure_quality=false;
|
||
|
bool show_ssim_map=false;
|
||
|
bool show_psnr_map=false;
|
||
|
const char* reference_filename;
|
||
|
FILE* reference_file;
|
||
|
int highestTID = 100;
|
||
|
int verbosity=0;
|
||
|
int disable_deblocking=0;
|
||
|
int disable_sao=0;
|
||
|
|
||
|
static struct option long_options[] = {
|
||
|
{"quiet", no_argument, 0, 'q' },
|
||
|
{"threads", required_argument, 0, 't' },
|
||
|
{"check-hash", no_argument, 0, 'c' },
|
||
|
{"profile", no_argument, 0, 'p' },
|
||
|
{"frames", required_argument, 0, 'f' },
|
||
|
{"output", required_argument, 0, 'o' },
|
||
|
{"dump", no_argument, 0, 'd' },
|
||
|
{"nal", no_argument, 0, 'n' },
|
||
|
{"videogfx", no_argument, 0, 'V' },
|
||
|
{"no-logging", no_argument, 0, 'L' },
|
||
|
{"help", no_argument, 0, 'h' },
|
||
|
{"noaccel", no_argument, 0, '0' },
|
||
|
{"write-bytestream", required_argument,0, 'B' },
|
||
|
{"measure", required_argument, 0, 'm' },
|
||
|
{"ssim", no_argument, 0, 's' },
|
||
|
{"errmap", no_argument, 0, 'e' },
|
||
|
{"highest-TID", required_argument, 0, 'T' },
|
||
|
{"verbose", no_argument, 0, 'v' },
|
||
|
{"disable-deblocking", no_argument, &disable_deblocking, 1 },
|
||
|
{"disable-sao", no_argument, &disable_sao, 1 },
|
||
|
{0, 0, 0, 0 }
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
static void write_picture(const de265_image* img)
|
||
|
{
|
||
|
static FILE* fh = NULL;
|
||
|
if (fh==NULL) {
|
||
|
if (strcmp(output_filename, "-") == 0) {
|
||
|
fh = stdout;
|
||
|
} else {
|
||
|
fh = fopen(output_filename, "wb");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (int c=0;c<3;c++) {
|
||
|
int stride;
|
||
|
const uint8_t* p = de265_get_image_plane(img, c, &stride);
|
||
|
|
||
|
int width = de265_get_image_width(img,c);
|
||
|
|
||
|
if (de265_get_bits_per_pixel(img,c)<=8) {
|
||
|
// --- save 8 bit YUV ---
|
||
|
|
||
|
for (int y=0;y<de265_get_image_height(img,c);y++) {
|
||
|
fwrite(p + y*stride, width, 1, fh);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// --- save 16 bit YUV ---
|
||
|
|
||
|
int bpp = (de265_get_bits_per_pixel(img,c)+7)/8;
|
||
|
int pixelsPerLine = stride/bpp;
|
||
|
|
||
|
uint8_t* buf = new uint8_t[width*2];
|
||
|
uint16_t* p16 = (uint16_t*)p;
|
||
|
|
||
|
for (int y=0;y<de265_get_image_height(img,c);y++) {
|
||
|
for (int x=0;x<width;x++) {
|
||
|
uint16_t pixel_value = (p16+y*pixelsPerLine)[x];
|
||
|
buf[2*x+0] = pixel_value & 0xFF;
|
||
|
buf[2*x+1] = pixel_value >> 8;
|
||
|
}
|
||
|
|
||
|
fwrite(buf, width*2, 1, fh);
|
||
|
}
|
||
|
|
||
|
delete[] buf;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fflush(fh);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
#if HAVE_VIDEOGFX
|
||
|
void display_image(const struct de265_image* img)
|
||
|
{
|
||
|
static X11Win win;
|
||
|
|
||
|
// display picture
|
||
|
|
||
|
static bool first=true;
|
||
|
|
||
|
if (first) {
|
||
|
first=false;
|
||
|
win.Create(de265_get_image_width(img,0),
|
||
|
de265_get_image_height(img,0),
|
||
|
"de265 output");
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
int width = de265_get_image_width(img,0);
|
||
|
int height = de265_get_image_height(img,0);
|
||
|
de265_chroma chroma = de265_get_chroma_format(img);
|
||
|
|
||
|
ChromaFormat vgfx_chroma;
|
||
|
Colorspace vgfx_cs = Colorspace_YUV;
|
||
|
|
||
|
switch (chroma) {
|
||
|
case de265_chroma_420: vgfx_chroma = Chroma_420; break;
|
||
|
case de265_chroma_422: vgfx_chroma = Chroma_422; break;
|
||
|
case de265_chroma_444: vgfx_chroma = Chroma_444; break;
|
||
|
case de265_chroma_mono: vgfx_cs = Colorspace_Greyscale; break;
|
||
|
}
|
||
|
|
||
|
Image<Pixel> visu;
|
||
|
visu.Create(width, height, vgfx_cs, vgfx_chroma);
|
||
|
|
||
|
int nChannels = 3;
|
||
|
if (chroma == de265_chroma_mono) {
|
||
|
nChannels = 1;
|
||
|
}
|
||
|
|
||
|
for (int ch=0;ch<nChannels;ch++) {
|
||
|
const uint8_t* data;
|
||
|
int stride;
|
||
|
|
||
|
data = de265_get_image_plane(img,ch,&stride);
|
||
|
width = de265_get_image_width(img,ch);
|
||
|
height = de265_get_image_height(img,ch);
|
||
|
|
||
|
int bit_depth = de265_get_bits_per_pixel(img,ch);
|
||
|
|
||
|
if (bit_depth==8) {
|
||
|
for (int y=0;y<height;y++) {
|
||
|
memcpy(visu.AskFrame((BitmapChannel)ch)[y], data + y*stride, width);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
const uint16_t* data16 = (const uint16_t*)data;
|
||
|
for (int y=0;y<height;y++) {
|
||
|
for (int x=0;x<width;x++) {
|
||
|
visu.AskFrame((BitmapChannel)ch)[y][x] = *(data16 + y*stride +x) >> (bit_depth-8);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
win.Display(visu);
|
||
|
win.WaitForKeypress();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static uint8_t* convert_to_8bit(const uint8_t* data, int width, int height,
|
||
|
int pixelsPerLine, int bit_depth)
|
||
|
{
|
||
|
const uint16_t* data16 = (const uint16_t*)data;
|
||
|
uint8_t* out = new uint8_t[pixelsPerLine*height];
|
||
|
|
||
|
for (int y=0;y<height;y++) {
|
||
|
for (int x=0;x<width;x++) {
|
||
|
out[y*pixelsPerLine + x] = *(data16 + y*pixelsPerLine +x) >> (bit_depth-8);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
|
||
|
#if HAVE_SDL
|
||
|
SDL_YUV_Display sdlWin;
|
||
|
bool sdl_active=false;
|
||
|
|
||
|
bool display_sdl(const struct de265_image* img)
|
||
|
{
|
||
|
int width = de265_get_image_width(img,0);
|
||
|
int height = de265_get_image_height(img,0);
|
||
|
|
||
|
int chroma_width = de265_get_image_width(img,1);
|
||
|
int chroma_height = de265_get_image_height(img,1);
|
||
|
|
||
|
de265_chroma chroma = de265_get_chroma_format(img);
|
||
|
|
||
|
if (!sdl_active) {
|
||
|
sdl_active=true;
|
||
|
enum SDL_YUV_Display::SDL_Chroma sdlChroma;
|
||
|
switch (chroma) {
|
||
|
case de265_chroma_420: sdlChroma = SDL_YUV_Display::SDL_CHROMA_420; break;
|
||
|
case de265_chroma_422: sdlChroma = SDL_YUV_Display::SDL_CHROMA_422; break;
|
||
|
case de265_chroma_444: sdlChroma = SDL_YUV_Display::SDL_CHROMA_444; break;
|
||
|
case de265_chroma_mono: sdlChroma = SDL_YUV_Display::SDL_CHROMA_MONO; break;
|
||
|
}
|
||
|
|
||
|
sdlWin.init(width,height, sdlChroma);
|
||
|
}
|
||
|
|
||
|
int stride,chroma_stride;
|
||
|
const uint8_t* y = de265_get_image_plane(img,0,&stride);
|
||
|
const uint8_t* cb =de265_get_image_plane(img,1,&chroma_stride);
|
||
|
const uint8_t* cr =de265_get_image_plane(img,2,NULL);
|
||
|
|
||
|
int bpp_y = (de265_get_bits_per_pixel(img,0)+7)/8;
|
||
|
int bpp_c = (de265_get_bits_per_pixel(img,1)+7)/8;
|
||
|
int ppl_y = stride/bpp_y;
|
||
|
int ppl_c = chroma_stride/bpp_c;
|
||
|
|
||
|
uint8_t* y16 = NULL;
|
||
|
uint8_t* cb16 = NULL;
|
||
|
uint8_t* cr16 = NULL;
|
||
|
int bd;
|
||
|
|
||
|
if ((bd=de265_get_bits_per_pixel(img, 0)) > 8) {
|
||
|
y16 = convert_to_8bit(y, width,height,ppl_y,bd); y=y16;
|
||
|
}
|
||
|
|
||
|
if (chroma != de265_chroma_mono) {
|
||
|
if ((bd=de265_get_bits_per_pixel(img, 1)) > 8) {
|
||
|
cb16 = convert_to_8bit(cb, chroma_width,chroma_height,ppl_c,bd); cb=cb16;
|
||
|
}
|
||
|
if ((bd=de265_get_bits_per_pixel(img, 2)) > 8) {
|
||
|
cr16 = convert_to_8bit(cr, chroma_width,chroma_height,ppl_c,bd); cr=cr16;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sdlWin.display(y,cb,cr, ppl_y, ppl_c);
|
||
|
|
||
|
delete[] y16;
|
||
|
delete[] cb16;
|
||
|
delete[] cr16;
|
||
|
|
||
|
return sdlWin.doQuit();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
static int width,height;
|
||
|
static uint32_t framecnt=0;
|
||
|
|
||
|
bool output_image(const de265_image* img)
|
||
|
{
|
||
|
bool stop=false;
|
||
|
|
||
|
width = de265_get_image_width(img,0);
|
||
|
height = de265_get_image_height(img,0);
|
||
|
|
||
|
framecnt++;
|
||
|
//printf("SHOW POC: %d / PTS: %ld / integrity: %d\n",img->PicOrderCntVal, img->pts, img->integrity);
|
||
|
|
||
|
|
||
|
if (0) {
|
||
|
const char* nal_unit_name;
|
||
|
int nuh_layer_id;
|
||
|
int nuh_temporal_id;
|
||
|
de265_get_image_NAL_header(img, NULL, &nal_unit_name, &nuh_layer_id, &nuh_temporal_id);
|
||
|
|
||
|
printf("NAL: %s layer:%d temporal:%d\n",nal_unit_name, nuh_layer_id, nuh_temporal_id);
|
||
|
}
|
||
|
|
||
|
|
||
|
if (!quiet) {
|
||
|
#if HAVE_SDL && HAVE_VIDEOGFX
|
||
|
if (output_with_videogfx) {
|
||
|
display_image(img);
|
||
|
} else {
|
||
|
stop = display_sdl(img);
|
||
|
}
|
||
|
#elif HAVE_SDL
|
||
|
stop = display_sdl(img);
|
||
|
#elif HAVE_VIDEOGFX
|
||
|
display_image(img);
|
||
|
#endif
|
||
|
}
|
||
|
if (write_yuv) {
|
||
|
write_picture(img);
|
||
|
}
|
||
|
|
||
|
if ((framecnt%100)==0) {
|
||
|
fprintf(stderr,"frame %d\r",framecnt);
|
||
|
}
|
||
|
|
||
|
if (framecnt>=max_frames) {
|
||
|
stop=true;
|
||
|
}
|
||
|
|
||
|
return stop;
|
||
|
}
|
||
|
|
||
|
|
||
|
static double mse_y=0.0, mse_cb=0.0, mse_cr=0.0;
|
||
|
static int mse_frames=0;
|
||
|
|
||
|
static double ssim_y=0.0;
|
||
|
static int ssim_frames=0;
|
||
|
|
||
|
void measure(const de265_image* img)
|
||
|
{
|
||
|
// --- compute PSNR ---
|
||
|
|
||
|
int width = de265_get_image_width(img,0);
|
||
|
int height = de265_get_image_height(img,0);
|
||
|
|
||
|
uint8_t* p = (uint8_t*)malloc(width*height*3/2);
|
||
|
if (p == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
size_t toread = width*height*3/2;
|
||
|
if (fread(p,1,toread,reference_file) != toread) {
|
||
|
free(p);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int stride, cstride;
|
||
|
const uint8_t* yptr = de265_get_image_plane(img,0, &stride);
|
||
|
const uint8_t* cbptr = de265_get_image_plane(img,1, &cstride);
|
||
|
const uint8_t* crptr = de265_get_image_plane(img,2, &cstride);
|
||
|
|
||
|
double img_mse_y = MSE( yptr, stride, p, width, width, height);
|
||
|
double img_mse_cb = MSE(cbptr, cstride, p+width*height, width/2, width/2,height/2);
|
||
|
double img_mse_cr = MSE(crptr, cstride, p+width*height*5/4, width/2, width/2,height/2);
|
||
|
|
||
|
mse_frames++;
|
||
|
|
||
|
mse_y += img_mse_y;
|
||
|
mse_cb += img_mse_cb;
|
||
|
mse_cr += img_mse_cr;
|
||
|
|
||
|
|
||
|
|
||
|
// --- compute SSIM ---
|
||
|
|
||
|
double ssimSum = 0.0;
|
||
|
|
||
|
#if HAVE_VIDEOGFX
|
||
|
Bitmap<Pixel> ref, coded;
|
||
|
ref .Create(width, height); // reference image
|
||
|
coded.Create(width, height); // coded image
|
||
|
|
||
|
const uint8_t* data;
|
||
|
data = de265_get_image_plane(img,0,&stride);
|
||
|
|
||
|
for (int y=0;y<height;y++) {
|
||
|
memcpy(coded[y], data + y*stride, width);
|
||
|
memcpy(ref[y], p + y*stride, width);
|
||
|
}
|
||
|
|
||
|
SSIM ssimAlgo;
|
||
|
Bitmap<float> ssim = ssimAlgo.calcSSIM(ref,coded);
|
||
|
|
||
|
Bitmap<Pixel> ssimMap;
|
||
|
ssimMap.Create(width,height);
|
||
|
|
||
|
for (int y=0;y<height;y++)
|
||
|
for (int x=0;x<width;x++)
|
||
|
{
|
||
|
float v = ssim[y][x];
|
||
|
ssimSum += v;
|
||
|
v = v*v;
|
||
|
v = 255*v; //pow(v, 20);
|
||
|
|
||
|
//assert(v<=255.0);
|
||
|
ssimMap[y][x] = v;
|
||
|
}
|
||
|
|
||
|
ssimSum /= width*height;
|
||
|
|
||
|
|
||
|
Bitmap<Pixel> error_map = CalcErrorMap(ref, coded, TransferCurve_Sqrt);
|
||
|
|
||
|
|
||
|
// display PSNR error map
|
||
|
|
||
|
if (show_psnr_map) {
|
||
|
static X11Win win;
|
||
|
static bool first=true;
|
||
|
|
||
|
if (first) {
|
||
|
first=false;
|
||
|
win.Create(de265_get_image_width(img,0),
|
||
|
de265_get_image_height(img,0),
|
||
|
"psnr output");
|
||
|
}
|
||
|
|
||
|
win.Display(MakeImage(error_map));
|
||
|
}
|
||
|
|
||
|
|
||
|
// display SSIM error map
|
||
|
|
||
|
if (show_ssim_map) {
|
||
|
static X11Win win;
|
||
|
static bool first=true;
|
||
|
|
||
|
if (first) {
|
||
|
first=false;
|
||
|
win.Create(de265_get_image_width(img,0),
|
||
|
de265_get_image_height(img,0),
|
||
|
"ssim output");
|
||
|
}
|
||
|
|
||
|
win.Display(MakeImage(ssimMap));
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
ssim_frames++;
|
||
|
ssim_y += ssimSum;
|
||
|
|
||
|
printf("%5d %6f %6f %6f %6f\n",
|
||
|
framecnt,
|
||
|
PSNR(img_mse_y), PSNR(img_mse_cb), PSNR(img_mse_cr),
|
||
|
ssimSum);
|
||
|
|
||
|
free(p);
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef WIN32
|
||
|
#include <time.h>
|
||
|
#define WIN32_LEAN_AND_MEAN
|
||
|
#include <winsock.h>
|
||
|
int gettimeofday(struct timeval *tp, void *)
|
||
|
{
|
||
|
time_t clock;
|
||
|
struct tm tm;
|
||
|
SYSTEMTIME wtm;
|
||
|
|
||
|
GetLocalTime(&wtm);
|
||
|
tm.tm_year = wtm.wYear - 1900;
|
||
|
tm.tm_mon = wtm.wMonth - 1;
|
||
|
tm.tm_mday = wtm.wDay;
|
||
|
tm.tm_hour = wtm.wHour;
|
||
|
tm.tm_min = wtm.wMinute;
|
||
|
tm.tm_sec = wtm.wSecond;
|
||
|
tm. tm_isdst = -1;
|
||
|
clock = mktime(&tm);
|
||
|
tp->tv_sec = (long) clock;
|
||
|
tp->tv_usec = wtm.wMilliseconds * 1000;
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef HAVE___MALLOC_HOOK
|
||
|
#ifdef __GNUC__
|
||
|
#pragma GCC diagnostic push
|
||
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||
|
#endif
|
||
|
static void *(*old_malloc_hook)(size_t, const void *);
|
||
|
|
||
|
static void *new_malloc_hook(size_t size, const void *caller) {
|
||
|
void *mem;
|
||
|
|
||
|
/*
|
||
|
if (size>1000000) {
|
||
|
raise(SIGINT);
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
__malloc_hook = old_malloc_hook;
|
||
|
mem = malloc(size);
|
||
|
fprintf(stderr, "%p: malloc(%zu) = %p\n", caller, size, mem);
|
||
|
__malloc_hook = new_malloc_hook;
|
||
|
|
||
|
return mem;
|
||
|
}
|
||
|
|
||
|
static void init_my_hooks(void) {
|
||
|
old_malloc_hook = __malloc_hook;
|
||
|
__malloc_hook = new_malloc_hook;
|
||
|
}
|
||
|
|
||
|
#if DO_MEMORY_LOGGING
|
||
|
void (*volatile __malloc_initialize_hook)(void) = init_my_hooks;
|
||
|
#endif
|
||
|
#ifdef __GNUC__
|
||
|
#pragma GCC diagnostic pop
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
|
||
|
int main(int argc, char** argv)
|
||
|
{
|
||
|
while (1) {
|
||
|
int option_index = 0;
|
||
|
|
||
|
int c = getopt_long(argc, argv, "qt:chf:o:dLB:n0vT:m:se"
|
||
|
#if HAVE_VIDEOGFX && HAVE_SDL
|
||
|
"V"
|
||
|
#endif
|
||
|
, long_options, &option_index);
|
||
|
if (c == -1)
|
||
|
break;
|
||
|
|
||
|
switch (c) {
|
||
|
case 'q': quiet++; break;
|
||
|
case 't': nThreads=atoi(optarg); break;
|
||
|
case 'c': check_hash=true; break;
|
||
|
case 'f': max_frames=atoi(optarg); break;
|
||
|
case 'o': write_yuv=true; output_filename=optarg; break;
|
||
|
case 'h': show_help=true; break;
|
||
|
case 'd': dump_headers=true; break;
|
||
|
case 'n': nal_input=true; break;
|
||
|
case 'V': output_with_videogfx=true; break;
|
||
|
case 'L': logging=false; break;
|
||
|
case '0': no_acceleration=true; break;
|
||
|
case 'B': write_bytestream=true; bytestream_filename=optarg; break;
|
||
|
case 'm': measure_quality=true; reference_filename=optarg; break;
|
||
|
case 's': show_ssim_map=true; break;
|
||
|
case 'e': show_psnr_map=true; break;
|
||
|
case 'T': highestTID=atoi(optarg); break;
|
||
|
case 'v': verbosity++; break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (optind != argc-1 || show_help) {
|
||
|
fprintf(stderr," dec265 v%s\n", de265_get_version());
|
||
|
fprintf(stderr,"--------------\n");
|
||
|
fprintf(stderr,"usage: dec265 [options] videofile.bin\n");
|
||
|
fprintf(stderr,"The video file must be a raw bitstream, or a stream with NAL units (option -n).\n");
|
||
|
fprintf(stderr,"\n");
|
||
|
fprintf(stderr,"options:\n");
|
||
|
fprintf(stderr," -q, --quiet do not show decoded image\n");
|
||
|
fprintf(stderr," -t, --threads N set number of worker threads (0 - no threading)\n");
|
||
|
fprintf(stderr," -c, --check-hash perform hash check\n");
|
||
|
fprintf(stderr," -n, --nal input is a stream with 4-byte length prefixed NAL units\n");
|
||
|
fprintf(stderr," -f, --frames N set number of frames to process\n");
|
||
|
fprintf(stderr," -o, --output write YUV reconstruction\n");
|
||
|
fprintf(stderr," -d, --dump dump headers\n");
|
||
|
#if HAVE_VIDEOGFX && HAVE_SDL
|
||
|
fprintf(stderr," -V, --videogfx output with videogfx instead of SDL\n");
|
||
|
#endif
|
||
|
fprintf(stderr," -0, --noaccel do not use any accelerated code (SSE)\n");
|
||
|
fprintf(stderr," -v, --verbose increase verbosity level (up to 3 times)\n");
|
||
|
fprintf(stderr," -L, --no-logging disable logging\n");
|
||
|
fprintf(stderr," -B, --write-bytestream FILENAME write raw bytestream (from NAL input)\n");
|
||
|
fprintf(stderr," -m, --measure YUV compute PSNRs relative to reference YUV\n");
|
||
|
#if HAVE_VIDEOGFX
|
||
|
fprintf(stderr," -s, --ssim show SSIM-map (only when -m active)\n");
|
||
|
fprintf(stderr," -e, --errmap show error-map (only when -m active)\n");
|
||
|
#endif
|
||
|
fprintf(stderr," -T, --highest-TID select highest temporal sublayer to decode\n");
|
||
|
fprintf(stderr," --disable-deblocking disable deblocking filter\n");
|
||
|
fprintf(stderr," --disable-sao disable sample-adaptive offset filter\n");
|
||
|
fprintf(stderr," -h, --help show help\n");
|
||
|
|
||
|
exit(show_help ? 0 : 5);
|
||
|
}
|
||
|
|
||
|
|
||
|
de265_error err =DE265_OK;
|
||
|
|
||
|
de265_decoder_context* ctx = de265_new_decoder();
|
||
|
|
||
|
de265_set_parameter_bool(ctx, DE265_DECODER_PARAM_BOOL_SEI_CHECK_HASH, check_hash);
|
||
|
de265_set_parameter_bool(ctx, DE265_DECODER_PARAM_SUPPRESS_FAULTY_PICTURES, false);
|
||
|
|
||
|
de265_set_parameter_bool(ctx, DE265_DECODER_PARAM_DISABLE_DEBLOCKING, disable_deblocking);
|
||
|
de265_set_parameter_bool(ctx, DE265_DECODER_PARAM_DISABLE_SAO, disable_sao);
|
||
|
|
||
|
if (dump_headers) {
|
||
|
de265_set_parameter_int(ctx, DE265_DECODER_PARAM_DUMP_SPS_HEADERS, 1);
|
||
|
de265_set_parameter_int(ctx, DE265_DECODER_PARAM_DUMP_VPS_HEADERS, 1);
|
||
|
de265_set_parameter_int(ctx, DE265_DECODER_PARAM_DUMP_PPS_HEADERS, 1);
|
||
|
de265_set_parameter_int(ctx, DE265_DECODER_PARAM_DUMP_SLICE_HEADERS, 1);
|
||
|
}
|
||
|
|
||
|
if (no_acceleration) {
|
||
|
de265_set_parameter_int(ctx, DE265_DECODER_PARAM_ACCELERATION_CODE, de265_acceleration_SCALAR);
|
||
|
}
|
||
|
|
||
|
if (!logging) {
|
||
|
de265_disable_logging();
|
||
|
}
|
||
|
|
||
|
de265_set_verbosity(verbosity);
|
||
|
|
||
|
|
||
|
if (argc>=3) {
|
||
|
if (nThreads>0) {
|
||
|
err = de265_start_worker_threads(ctx, nThreads);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
de265_set_limit_TID(ctx, highestTID);
|
||
|
|
||
|
|
||
|
if (measure_quality) {
|
||
|
reference_file = fopen(reference_filename, "rb");
|
||
|
}
|
||
|
|
||
|
|
||
|
FILE* fh;
|
||
|
if (strcmp(argv[optind],"-")==0) {
|
||
|
fh = stdin;
|
||
|
}
|
||
|
else {
|
||
|
fh = fopen(argv[optind], "rb");
|
||
|
}
|
||
|
|
||
|
if (fh==NULL) {
|
||
|
fprintf(stderr,"cannot open file %s!\n", argv[optind]);
|
||
|
exit(10);
|
||
|
}
|
||
|
|
||
|
FILE* bytestream_fh = NULL;
|
||
|
|
||
|
if (write_bytestream) {
|
||
|
bytestream_fh = fopen(bytestream_filename, "wb");
|
||
|
}
|
||
|
|
||
|
bool stop=false;
|
||
|
|
||
|
struct timeval tv_start;
|
||
|
gettimeofday(&tv_start, NULL);
|
||
|
|
||
|
int pos=0;
|
||
|
|
||
|
while (!stop)
|
||
|
{
|
||
|
//tid = (framecnt/1000) & 1;
|
||
|
//de265_set_limit_TID(ctx, tid);
|
||
|
|
||
|
if (nal_input) {
|
||
|
uint8_t len[4];
|
||
|
int n = fread(len,1,4,fh);
|
||
|
int length = (len[0]<<24) + (len[1]<<16) + (len[2]<<8) + len[3];
|
||
|
|
||
|
uint8_t* buf = (uint8_t*)malloc(length);
|
||
|
n = fread(buf,1,length,fh);
|
||
|
err = de265_push_NAL(ctx, buf,n, pos, (void*)1);
|
||
|
|
||
|
if (write_bytestream) {
|
||
|
uint8_t sc[3] = { 0,0,1 };
|
||
|
fwrite(sc ,1,3,bytestream_fh);
|
||
|
fwrite(buf,1,n,bytestream_fh);
|
||
|
}
|
||
|
|
||
|
free(buf);
|
||
|
pos+=n;
|
||
|
}
|
||
|
else {
|
||
|
// read a chunk of input data
|
||
|
uint8_t buf[BUFFER_SIZE];
|
||
|
int n = fread(buf,1,BUFFER_SIZE,fh);
|
||
|
|
||
|
// decode input data
|
||
|
if (n) {
|
||
|
err = de265_push_data(ctx, buf, n, pos, (void*)2);
|
||
|
if (err != DE265_OK) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pos+=n;
|
||
|
|
||
|
if (0) { // fake skipping
|
||
|
if (pos>1000000) {
|
||
|
printf("RESET\n");
|
||
|
de265_reset(ctx);
|
||
|
pos=0;
|
||
|
|
||
|
fseek(fh,-200000,SEEK_CUR);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// printf("pending data: %d\n", de265_get_number_of_input_bytes_pending(ctx));
|
||
|
|
||
|
if (feof(fh)) {
|
||
|
err = de265_flush_data(ctx); // indicate end of stream
|
||
|
stop = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
// decoding / display loop
|
||
|
|
||
|
int more=1;
|
||
|
while (more)
|
||
|
{
|
||
|
more = 0;
|
||
|
|
||
|
// decode some more
|
||
|
|
||
|
err = de265_decode(ctx, &more);
|
||
|
if (err != DE265_OK) {
|
||
|
// if (quiet<=1) fprintf(stderr,"ERROR: %s\n", de265_get_error_text(err));
|
||
|
|
||
|
if (check_hash && err == DE265_ERROR_CHECKSUM_MISMATCH)
|
||
|
stop = 1;
|
||
|
more = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// show available images
|
||
|
|
||
|
const de265_image* img = de265_get_next_picture(ctx);
|
||
|
if (img) {
|
||
|
if (measure_quality) {
|
||
|
measure(img);
|
||
|
}
|
||
|
|
||
|
stop = output_image(img);
|
||
|
if (stop) more=0;
|
||
|
else more=1;
|
||
|
}
|
||
|
|
||
|
// show warnings
|
||
|
|
||
|
for (;;) {
|
||
|
de265_error warning = de265_get_warning(ctx);
|
||
|
if (warning==DE265_OK) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (quiet<=1) fprintf(stderr,"WARNING: %s\n", de265_get_error_text(warning));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fclose(fh);
|
||
|
|
||
|
if (write_bytestream) {
|
||
|
fclose(bytestream_fh);
|
||
|
}
|
||
|
|
||
|
if (measure_quality) {
|
||
|
printf("#total %6f %6f %6f %6f\n",
|
||
|
PSNR(mse_y /mse_frames),
|
||
|
PSNR(mse_cb/mse_frames),
|
||
|
PSNR(mse_cr/mse_frames),
|
||
|
ssim_y/ssim_frames);
|
||
|
|
||
|
fclose(reference_file);
|
||
|
}
|
||
|
|
||
|
de265_free_decoder(ctx);
|
||
|
|
||
|
struct timeval tv_end;
|
||
|
gettimeofday(&tv_end, NULL);
|
||
|
|
||
|
if (err != DE265_OK) {
|
||
|
if (quiet<=1) fprintf(stderr,"decoding error: %s (code=%d)\n", de265_get_error_text(err), err);
|
||
|
}
|
||
|
|
||
|
double secs = tv_end.tv_sec-tv_start.tv_sec;
|
||
|
secs += (tv_end.tv_usec - tv_start.tv_usec)*0.001*0.001;
|
||
|
|
||
|
if (quiet<=1) fprintf(stderr,"nFrames decoded: %d (%dx%d @ %5.2f fps)\n",framecnt,
|
||
|
width,height,framecnt/secs);
|
||
|
|
||
|
|
||
|
return err==DE265_OK ? 0 : 10;
|
||
|
}
|