Related
So I am trying to use libpng to write png's from an array of unsigned char that are a bit depth of 1. Meaning for all bits, there is only black and white, as a gray scale image. I have managed to do this successfully for 8-bit depth gray scale png, but not 1 bit depth. Here is the code i have :
extern int write_png_bwsq(const char* filename,
int dimen,
const unsigned char *buffer,
char* title)
{
int yrow;
int dim_bytes;
int code = 1;
FILE *fp = NULL;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
png_bytep row = NULL;
dim_bytes = (dimen * dimen) / 8;
// Open file for writing (binary mode)
fp = fopen(filename, "wb");
if (fp == NULL) {
fprintf(stderr, "PNG ERROR: Could not open file %s for writing\n", filename);
code = 0;
goto finalise;
}
// Initialize write structure
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
fprintf(stderr, "PNG ERROR: Could not allocate PNG write struct\n");
code = 0;
goto finalise;
}
// Initialize info structure
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
fprintf(stderr, "PNG ERROR: Could not allocate PNG info struct\n");
code = 0;
goto finalise;
}
// Setup Exception handling
if (setjmp(png_jmpbuf(png_ptr))) {
fprintf(stderr, "PNG Error: Creating the PNG output failed.\n");
code = 0;
goto finalise;
}
png_init_io(png_ptr, fp);
png_set_IHDR(png_ptr, info_ptr, dimen, dimen,
1, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
// Sets the title
if (title != NULL) {
png_text title_text;
title_text.compression = PNG_TEXT_COMPRESSION_NONE;
title_text.key = "Title";
title_text.text = title;
png_set_text(png_ptr, info_ptr, &title_text, 1);
}
png_write_info(png_ptr, info_ptr);
row = (png_bytep) buffer;
// Write image data
for (yrow=0 ; yrow<dim_bytes ; yrow++) {
png_write_row(png_ptr, row);
++row;
}
// End write operation
png_write_end(png_ptr, NULL);
finalise:
if (fp != NULL) fclose(fp);
if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
return code;
}
Then, i have a separate file where i prepare my image
#include "write_pngs.h"
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#if CHAR_BIT != 8
# error "CHAR_BIT is not usable on this platform"
#endif
#define PIC_SIZE_DIM 1080
#define PIC_SIZE_BW ((PIC_SIZE_DIM * PIC_SIZE_DIM) / CHAR_BIT)
static unsigned char PIC_BITS[PIC_SIZE_BW] = {0};
static void __write_bits_pic(void)
{
size_t i = 1000;
for(;i < 140000;++i) {
PIC_BITS[i] = ((i + 76) >> 5) & 0xff;
}
}
int main(void) {
__write_bits_pic();
printf("Writing picture of %d bytes and %d bits\n", PIC_SIZE_BW, PIC_SIZE_BW * CHAR_BIT);
return !write_png_bwsq("bwpixs.png",
PIC_SIZE_DIM,
PIC_BITS,
"bwpixs");
}
This results in an incorrect image where its not only quite large for a png (about 5mb for only 1080 x 1080), but only the bottom right corner of the image is changed from black.
What am i doing wrong here ? Does libpng require any special steps for writing pngs that are only 1 in bit depth for gray scale i am not doing ?
You are calling png_write_row() way too many times. Your
for (yrow=0 ; yrow<dim_bytes ; yrow++) {
png_write_row(png_ptr, row);
++row;
}
should be something like:
for (yrow=0 ; yrow<dim_bytes ; yrow += dimen / 8) {
png_write_row(png_ptr, row);
row += dimen / 8;
}
in order to write a row of 1080 pixels (135 bytes). You were calling png_write_row() for every byte, as if the image is eight pixels wide. Which it isn't.
I'm trying to get a grasp of basic features in libpng. To do this, I've used this snippet and adapted to my own example.
int x, y;
png_byte color_type = PNG_COLOR_TYPE_RGBA;
png_byte bit_depth = 16;
png_structp png_ptr;
png_infop info_ptr;
auto create_image(const int height, const int width) {
png_byte **rows = new png_byte *[height];
for (auto i = 0; i < height; i++)
rows[i] = new png_byte[width * 4];
return rows;
}
auto modify_image(const int height, const int width, png_byte **rows) {
for (auto i = 0; i < height; i++) {
for (auto j = 0; j < width; j++) {
// Red channel
rows[i][j * 4 + 0] = (j * 127.) / width;
// Blue channel
rows[i][j * 4 + 2] = (i * 127.) / height;
// Alpha channel
rows[i][j * 4 + 3] = 127;
}
}
}
void write(const std::string& filename, const int height, const int width, png_byte** rows)
{
/* create file */
FILE *fp = fopen(filename.c_str(), "wb");
if (!fp)
abort_("[write_png_file] File %s could not be opened for writing", filename);
/* initialize stuff */
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
abort_("[write_png_file] png_create_write_struct failed");
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
abort_("[write_png_file] png_create_info_struct failed");
if (setjmp(png_jmpbuf(png_ptr)))
abort_("[write_png_file] Error during init_io");
png_init_io(png_ptr, fp);
/* write header */
if (setjmp(png_jmpbuf(png_ptr)))
abort_("[write_png_file] Error during writing header");
png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_write_info(png_ptr, info_ptr);
/* write bytes */
if (setjmp(png_jmpbuf(png_ptr)))
abort_("[write_png_file] Error during writing bytes");
png_write_image(png_ptr, rows);
/* end write */
if (setjmp(png_jmpbuf(png_ptr)))
abort_("[write_png_file] Error during end of write");
png_write_end(png_ptr, NULL);
fclose(fp);
}
Though, when I run my code:
void render(const int height, const int width, const std::string &filename) {
png_byte **rows = create_image(height, width);
modify_image(height, width, rows);
write(filename, height, width, rows);
}
The problem is... I don't get the expected result at all. While I was expecting a square image with some kind of gradient I get... two rectangular rectangles.
Also, I've noticed that these two rectangles are stretched: while trying to render a circle, I've found that the circle was distorted and doubling its width makes it an actual circle...
Finally, I've seen that on the second rectangle, the last row seems to be random data (which is not the case on the first rectangle).
Please suggest if you have any ideas.
You create image with 16 bit depth yet use 1 byte per channel. Output image consists of odd / even rows of your original image being put on the same row. Basically each line of the right rectangle is caused by a buffer overrun. You need to allocate buffers that are twice as big, that is width * 4 * 2 and fill higher and lower bytes of each channel separately.
One of the things I'm trying to achieve is parallel encoding via FFmpeg's c API. This looks to work out of the box quite nicely; however, I've changed the goal posts slightly:
In an existing application, I already have a thread pool at hand. Instead of using another thread pool via FFmpeg, I would like reuse the existing thread pool in my application. Having studied the latest FFmpeg trunk docs, it very much looks possible.
Using some FFmpeg sample code, I've created a sample application to demonstrate what I'm trying to achieve (see below). The sample app generates a video-only mpeg2 ts using the mp2v codec.
The problem I'm experiencing is that the custom 'thread_execute' or 'thread_execute2' are never invoked. This is despite the fact that the codec appears to indicate that threading is supported. Please be aware that I have not yet plumbed in the thread pool just yet. My first goal is for it to call the custom function pointer.
I've tried to get assistance on the FFmpeg mailing lists but to no avail.
#include <iostream>
#include <thread>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <cstring>
#include <future>
extern "C"
{
#include <libavutil/avassert.h>
#include <libavutil/channel_layout.h>
#include <libavutil/opt.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
//#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
}
#define STREAM_DURATION 1000.0
#define STREAM_FRAME_RATE 25 /* 25 images/s */
#define STREAM_PIX_FMT AV_PIX_FMT_YUV420P /* default pix_fmt */
#define SCALE_FLAGS SWS_BICUBIC
// a wrapper around a single output AVStream
typedef struct OutputStream {
AVStream *st;
AVCodecContext *enc;
/* pts of the next frame that will be generated */
int64_t next_pts;
int samples_count;
AVFrame *frame;
AVFrame *tmp_frame;
float t, tincr, tincr2;
struct SwsContext *sws_ctx;
struct SwrContext *swr_ctx;
} OutputStream;
/////////////////////////////////////////////////////////////////////////////
// The ffmpeg variation raises compiler warnings.
char *cb_av_ts2str(char *buf, int64_t ts)
{
std::memset(buf,0,AV_TS_MAX_STRING_SIZE);
return av_ts_make_string(buf,ts);
}
/////////////////////////////////////////////////////////////////////////////
// The ffmpeg variation raises compiler warnings.
char *cb_av_ts2timestr(char *buf, int64_t ts, AVRational *tb)
{
std::memset(buf,0,sizeof(AV_TS_MAX_STRING_SIZE));
return av_ts_make_time_string(buf,ts,tb);
}
/////////////////////////////////////////////////////////////////////////////
// The ffmpeg variation raises compiler warnings.
char *cb_av_err2str(char *errbuf, size_t errbuf_size, int errnum)
{
std::memset(errbuf,0,errbuf_size);
return av_make_error_string(errbuf,errbuf_size,errnum);
}
int thread_execute(AVCodecContext* s, int (*func)(AVCodecContext *c2, void *arg2), void* arg, int* ret, int count, int size)
{
// Do it all serially for now
std::cout << "thread_execute" << std::endl;
for (int k = 0; k < count; ++k)
{
ret[k] = func(s, arg);
}
return 0;
}
int thread_execute2(AVCodecContext* s, int (*func)(AVCodecContext* c2, void* arg2, int, int), void* arg, int* ret, int count)
{
// Do it all serially for now
std::cout << "thread_execute2" << std::endl;
for (int k = 0; k < count; ++k)
{
ret[k] = func(s, arg, k, count);
}
return 0;
}
static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt)
{
char s[AV_TS_MAX_STRING_SIZE];
AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
printf("pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
cb_av_ts2str(s,pkt->pts), cb_av_ts2timestr(s,pkt->pts, time_base),
cb_av_ts2str(s,pkt->dts), cb_av_ts2timestr(s,pkt->dts, time_base),
cb_av_ts2str(s,pkt->duration), cb_av_ts2timestr(s,pkt->duration, time_base),
pkt->stream_index);
}
static int write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt)
{
/* rescale output packet timestamp values from codec to stream timebase */
av_packet_rescale_ts(pkt, *time_base, st->time_base);
pkt->stream_index = st->index;
/* Write the compressed frame to the media file. */
log_packet(fmt_ctx, pkt);
return av_interleaved_write_frame(fmt_ctx, pkt);
}
/* Add an output stream. */
static void add_stream(OutputStream *ost, AVFormatContext *oc,
AVCodec **codec,
enum AVCodecID codec_id)
{
AVCodecContext *c;
int i;
/* find the encoder */
*codec = avcodec_find_encoder(codec_id);
if (!(*codec)) {
fprintf(stderr, "Could not find encoder for '%s'\n",
avcodec_get_name(codec_id));
exit(1);
}
ost->st = avformat_new_stream(oc, NULL);
if (!ost->st) {
fprintf(stderr, "Could not allocate stream\n");
exit(1);
}
ost->st->id = oc->nb_streams-1;
c = avcodec_alloc_context3(*codec);
if (!c) {
fprintf(stderr, "Could not alloc an encoding context\n");
exit(1);
}
ost->enc = c;
switch ((*codec)->type)
{
case AVMEDIA_TYPE_AUDIO:
c->sample_fmt = (*codec)->sample_fmts ?
(*codec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
c->bit_rate = 64000;
c->sample_rate = 44100;
if ((*codec)->supported_samplerates) {
c->sample_rate = (*codec)->supported_samplerates[0];
for (i = 0; (*codec)->supported_samplerates[i]; i++) {
if ((*codec)->supported_samplerates[i] == 44100)
c->sample_rate = 44100;
}
}
c->channels = av_get_channel_layout_nb_channels(c->channel_layout);
c->channel_layout = AV_CH_LAYOUT_STEREO;
if ((*codec)->channel_layouts) {
c->channel_layout = (*codec)->channel_layouts[0];
for (i = 0; (*codec)->channel_layouts[i]; i++) {
if ((*codec)->channel_layouts[i] == AV_CH_LAYOUT_STEREO)
c->channel_layout = AV_CH_LAYOUT_STEREO;
}
}
c->channels = av_get_channel_layout_nb_channels(c->channel_layout);
ost->st->time_base = (AVRational){ 1, c->sample_rate };
break;
case AVMEDIA_TYPE_VIDEO:
c->codec_id = codec_id;
c->bit_rate = 400000;
/* Resolution must be a multiple of two. */
c->width = 352;
c->height = 288;
/* timebase: This is the fundamental unit of time (in seconds) in terms
* of which frame timestamps are represented. For fixed-fps content,
* timebase should be 1/framerate and timestamp increments should be
* identical to 1. */
ost->st->time_base = (AVRational){ 1, STREAM_FRAME_RATE };
c->time_base = ost->st->time_base;
c->gop_size = 12; /* emit one intra frame every twelve frames at most */
c->pix_fmt = STREAM_PIX_FMT;
if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
/* just for testing, we also add B-frames */
c->max_b_frames = 2;
}
if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) {
/* Needed to avoid using macroblocks in which some coeffs overflow.
* This does not happen with normal video, it just happens here as
* the motion of the chroma plane does not match the luma plane. */
c->mb_decision = 2;
}
break;
default:
break;
}
if (c->codec->capabilities & AV_CODEC_CAP_FRAME_THREADS ||
c->codec->capabilities & AV_CODEC_CAP_SLICE_THREADS)
{
if (c->codec->capabilities & AV_CODEC_CAP_FRAME_THREADS)
{
c->thread_type = FF_THREAD_FRAME;
}
if (c->codec->capabilities & AV_CODEC_CAP_SLICE_THREADS)
{
c->thread_type = FF_THREAD_SLICE;
}
c->execute = &thread_execute;
c->execute2 = &thread_execute2;
c->thread_count = 4;
// NOTE: Testing opaque.
c->opaque = (void*)0xff;
}
/* Some formats want stream headers to be separate. */
if (oc->oformat->flags & AVFMT_GLOBALHEADER)
c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
/**************************************************************/
/* video output */
static AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, int width, int height)
{
AVFrame *picture;
int ret;
picture = av_frame_alloc();
if (!picture)
return NULL;
picture->format = pix_fmt;
picture->width = width;
picture->height = height;
/* allocate the buffers for the frame data */
ret = av_frame_get_buffer(picture, 32);
if (ret < 0) {
fprintf(stderr, "Could not allocate frame data.\n");
exit(1);
}
return picture;
}
static void open_video(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg)
{
int ret;
AVCodecContext *c = ost->enc;
//AVDictionary *opt = NULL;
//av_dict_copy(&opt, opt_arg, 0);
/* open the codec */
ret = avcodec_open2(c, codec, NULL);
//av_dict_free(&opt);
if (ret < 0) {
char s[AV_ERROR_MAX_STRING_SIZE];
fprintf(stderr, "Could not open video codec: %s\n", cb_av_err2str(s,AV_ERROR_MAX_STRING_SIZE,ret));
exit(1);
}
/* allocate and init a re-usable frame */
ost->frame = alloc_picture(c->pix_fmt, c->width, c->height);
if (!ost->frame) {
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}
/* If the output format is not YUV420P, then a temporary YUV420P
* picture is needed too. It is then converted to the required
* output format. */
ost->tmp_frame = NULL;
if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
ost->tmp_frame = alloc_picture(AV_PIX_FMT_YUV420P, c->width, c->height);
if (!ost->tmp_frame) {
fprintf(stderr, "Could not allocate temporary picture\n");
exit(1);
}
}
/* copy the stream parameters to the muxer */
ret = avcodec_parameters_from_context(ost->st->codecpar, c);
if (ret < 0) {
fprintf(stderr, "Could not copy the stream parameters\n");
exit(1);
}
}
/* Prepare a dummy image. */
static void fill_yuv_image(AVFrame *pict, int frame_index,
int width, int height)
{
int x, y, i;
i = frame_index;
/* Y */
for (y = 0; y < height; y++)
for (x = 0; x < width; x++)
pict->data[0][y * pict->linesize[0] + x] = x + y + i * 3;
/* Cb and Cr */
for (y = 0; y < height / 2; y++) {
for (x = 0; x < width / 2; x++) {
pict->data[1][y * pict->linesize[1] + x] = 128 + y + i * 2;
pict->data[2][y * pict->linesize[2] + x] = 64 + x + i * 5;
}
}
}
static AVFrame *get_video_frame(OutputStream *ost)
{
AVCodecContext *c = ost->enc;
/* check if we want to generate more frames */
if (av_compare_ts(ost->next_pts, c->time_base,
STREAM_DURATION, (AVRational){ 1, 1 }) >= 0)
return NULL;
/* when we pass a frame to the encoder, it may keep a reference to it
* internally; make sure we do not overwrite it here */
if (av_frame_make_writable(ost->frame) < 0)
exit(1);
if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
/* as we only generate a YUV420P picture, we must convert it
* to the codec pixel format if needed */
/*if (!ost->sws_ctx) {
ost->sws_ctx = sws_getContext(c->width, c->height,
AV_PIX_FMT_YUV420P,
c->width, c->height,
c->pix_fmt,
SCALE_FLAGS, NULL, NULL, NULL);
if (!ost->sws_ctx) {
fprintf(stderr,
"Could not initialize the conversion context\n");
exit(1);
}
}
fill_yuv_image(ost->tmp_frame, ost->next_pts, c->width, c->height);
sws_scale(ost->sws_ctx,
(const uint8_t * const *)ost->tmp_frame->data, ost->tmp_frame->linesize,
0, c->height, ost->frame->data, ost->frame->linesize);*/
} else {
fill_yuv_image(ost->frame, ost->next_pts, c->width, c->height);
}
ost->frame->pts = ost->next_pts++;
return ost->frame;
}
/*
* encode one video frame and send it to the muxer
* return 1 when encoding is finished, 0 otherwise
*/
static int write_video_frame(AVFormatContext *oc, OutputStream *ost)
{
int ret;
AVCodecContext *c;
AVFrame *frame;
int got_packet = 0;
AVPacket pkt = { 0 };
c = ost->enc;
frame = get_video_frame(ost);
if (frame)
{
ret = avcodec_send_frame(ost->enc, frame);
if (ret < 0)
{
char s[AV_ERROR_MAX_STRING_SIZE];
fprintf(stderr, "Error encoding video frame: %s\n", cb_av_err2str(s, AV_ERROR_MAX_STRING_SIZE, ret));
exit(1);
}
}
av_init_packet(&pkt);
ret = avcodec_receive_packet(ost->enc,&pkt);
if (ret < 0)
{
if (ret == AVERROR(EAGAIN)) { ret = 0; }
else
{
char s[AV_ERROR_MAX_STRING_SIZE];
fprintf(stderr, "Error receiving packet: %s\n", cb_av_err2str(s,AV_ERROR_MAX_STRING_SIZE,ret));
exit(1);
}
}
else
{
got_packet = 1;
ret = write_frame(oc, &c->time_base, ost->st, &pkt);
}
if (ret < 0) {
char s[AV_ERROR_MAX_STRING_SIZE];
fprintf(stderr, "Error while writing video frame: %s\n", cb_av_err2str(s,AV_ERROR_MAX_STRING_SIZE,ret));
exit(1);
}
return (frame || got_packet) ? 0 : 1;
}
static void close_stream(AVFormatContext *oc, OutputStream *ost)
{
avcodec_free_context(&ost->enc);
av_frame_free(&ost->frame);
av_frame_free(&ost->tmp_frame);
//sws_freeContext(ost->sws_ctx);
//swr_free(&ost->swr_ctx);
}
/**************************************************************/
/* media file output */
int main(int argc, char **argv)
{
OutputStream video_st = { 0 }, audio_st = { 0 };
const char *filename;
AVOutputFormat *fmt;
AVFormatContext *oc;
AVCodec /**audio_codec,*/ *video_codec;
int ret;
int have_video = 0, have_audio = 0;
int encode_video = 0, encode_audio = 0;
AVDictionary *opt = NULL;
int i;
/* Initialize libavcodec, and register all codecs and formats. */
av_register_all();
avformat_network_init();
if (argc < 2) {
printf("usage: %s output_file\n"
"API example program to output a media file with libavformat.\n"
"This program generates a synthetic audio and video stream, encodes and\n"
"muxes them into a file named output_file.\n"
"The output format is automatically guessed according to the file extension.\n"
"Raw images can also be output by using '%%d' in the filename.\n"
"\n", argv[0]);
return 1;
}
filename = argv[1];
for (i = 2; i+1 < argc; i+=2) {
if (!strcmp(argv[i], "-flags") || !strcmp(argv[i], "-fflags"))
av_dict_set(&opt, argv[i]+1, argv[i+1], 0);
}
const char *pfilename = filename;
/* allocate the output media context */
avformat_alloc_output_context2(&oc, NULL, "mpegts", pfilename);
if (!oc) {
printf("Could not deduce output format from file extension: using MPEG.\n");
avformat_alloc_output_context2(&oc, NULL, "mpeg", pfilename);
}
if (!oc)
return 1;
fmt = oc->oformat;
/* Add the audio and video streams using the default format codecs
* and initialize the codecs. */
if (fmt->video_codec != AV_CODEC_ID_NONE) {
add_stream(&video_st, oc, &video_codec, fmt->video_codec);
have_video = 1;
encode_video = 1;
}
/*if (fmt->audio_codec != AV_CODEC_ID_NONE) {
add_stream(&audio_st, oc, &audio_codec, fmt->audio_codec);
have_audio = 1;
encode_audio = 1;
}*/
/* Now that all the parameters are set, we can open the audio and
* video codecs and allocate the necessary encode buffers. */
if (have_video)
open_video(oc, video_codec, &video_st, opt);
//if (have_audio)
// open_audio(oc, audio_codec, &audio_st, opt);
av_dump_format(oc, 0, pfilename, 1);
/* open the output file, if needed */
if (!(fmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&oc->pb, pfilename, AVIO_FLAG_WRITE);
if (ret < 0) {
char s[AV_ERROR_MAX_STRING_SIZE];
fprintf(stderr, "Could not open '%s': %s\n", pfilename,
cb_av_err2str(s,AV_ERROR_MAX_STRING_SIZE,ret));
return 1;
}
}
/* Write the stream header, if any. */
ret = avformat_write_header(oc, &opt);
if (ret < 0) {
char s[AV_ERROR_MAX_STRING_SIZE];
fprintf(stderr, "Error occurred when opening output file: %s\n",
cb_av_err2str(s,AV_ERROR_MAX_STRING_SIZE,ret));
return 1;
}
while (encode_video || encode_audio) {
/* select the stream to encode */
if (encode_video &&
(!encode_audio || av_compare_ts(video_st.next_pts, video_st.enc->time_base,
audio_st.next_pts, audio_st.enc->time_base) <= 0)) {
encode_video = !write_video_frame(oc, &video_st);
} else {
//encode_audio = !write_audio_frame(oc, &audio_st);
}
//std::this_thread::sleep_for(std::chrono::milliseconds(35));
}
/* Write the trailer, if any. The trailer must be written before you
* close the CodecContexts open when you wrote the header; otherwise
* av_write_trailer() may try to use memory that was freed on
* av_codec_close(). */
av_write_trailer(oc);
/* Close each codec. */
if (have_video)
close_stream(oc, &video_st);
if (have_audio)
close_stream(oc, &audio_st);
if (!(fmt->flags & AVFMT_NOFILE))
/* Close the output file. */
avio_closep(&oc->pb);
/* free the stream */
avformat_free_context(oc);
return 0;
}
//
Environment:
Ubuntu Zesty (17.04)
FFmpeg version 3.2.4 (via package manager)
gcc 6.3 (C++)
You have to do following:
call avcodec_alloc_context3(...). This call will set default execute and execute2 functions in new context
set c->thread_count = number_of_threads_in_your_thread_pool()
call avcodec_open2(...).
set c->execute and c->execute2 to point to your functions
call ff_thread_free(c). This function isnt exposed in libavcodec headers but you can add following line:
extern "C" void ff_thread_free(AVCodecContext *s);
Drawback is that libavcodec will create internal thread pool after avcodec_open2(...) call, and that pool will be deleted in ff_thread_free() call.
Internal thread pool is very efficient, but its not good if you plan to do parallel encoding of multiple video feeds. In that case libavcodec will create separate thread pool for each encoding video feed.
I am trying to compile a project using make command.
I have several occured errors about namespace std::.
I am using Ubuntu and I think I've installed all I need to compile C++ language.
Here's the code:
#include "texture.hpp"
#include "texturefetch.hpp"
#include <png.h>
#include <cstdio>
#include <iostream>
#include <cmath>
#include <assert.h>
#include <string>
Texture::~Texture(){
delete[] mPixels;
for (int i=1; i<mlodmax+1; ++i)
delete[] mMipMapLevels[i];
delete[] mMipMapLevels;
}
bool Texture::getclamp() const
{
return clamp;
}
void Texture::setclamp(bool value)
{
clamp = value;
}
int Texture::load(std::string filename){
FILE *fp;
png_byte magic[8];
/* open image file */
fp = std::fopen (filename.c_str(), "rb");
if (!fp) {
fprintf (stderr, "error: couldn't open \"%s\"!\n", filename.c_str());
return 0;
}
/* read magic number */
fread (magic, 1, sizeof (magic), fp);
/* check for valid magic number */
if (!png_check_sig (magic, sizeof (magic))) {
fprintf (stderr, "error: \"%s\" is not a valid PNG image!\n", filename.c_str());
fclose (fp);
return 0;
}
/* create a png read struct */
png_structp png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) {
fclose (fp);
return 0;
}
/* create a png info struct */
png_infop info_ptr = png_create_info_struct (png_ptr);
if (!info_ptr) {
fclose (fp);
png_destroy_read_struct (&png_ptr, NULL, NULL);
return 0;
}
/* initialize the setjmp for returning properly after a libpng
error occured */
if (setjmp (png_jmpbuf (png_ptr))) {
fclose (fp);
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
return 0;
}
/* setup libpng for using standard C fread() function
with our FILE pointer */
png_init_io (png_ptr, fp);
/* tell libpng that we have already read the magic number */
png_set_sig_bytes (png_ptr, sizeof (magic));
/* read png info */
png_read_info (png_ptr, info_ptr);
int bit_depth, color_type;
/* get some usefull information from header */
bit_depth = png_get_bit_depth (png_ptr, info_ptr);
color_type = png_get_color_type (png_ptr, info_ptr);
/* convert index color images to RGB images */
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb (png_ptr);
/* convert 1-2-4 bits grayscale images to 8 bits
grayscale. */
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand_gray_1_2_4_to_8(png_ptr);
if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
png_set_tRNS_to_alpha (png_ptr);
if (bit_depth == 16)
png_set_strip_16 (png_ptr);
else if (bit_depth < 8)
png_set_packing (png_ptr);
/* update info structure to apply transformations */
png_read_update_info (png_ptr, info_ptr);
/* retrieve updated information */
png_get_IHDR (png_ptr, info_ptr,
(png_uint_32*)(&mWidth),
(png_uint_32*)(&mHeight),
&bit_depth, &color_type,
NULL, NULL, NULL);
switch (color_type) {
case PNG_COLOR_TYPE_GRAY:
mPixelFormat = 1;
break;
case PNG_COLOR_TYPE_GRAY_ALPHA:
mPixelFormat = 2;
break;
case PNG_COLOR_TYPE_RGB:
mPixelFormat = 3;
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
mPixelFormat = 4;
break;
default:
/* Badness */
break;
}
/* we can now allocate memory for storing pixel data */
mPixels = new unsigned char[mWidth * mHeight * mPixelFormat];
png_bytep *row_pointers;
/* setup a pointer array. Each one points at the begening of a row. */
row_pointers = new png_bytep[mHeight];
for (int i = 0; i < mHeight; ++i) {
row_pointers[i] = (png_bytep)(mPixels +
((mHeight - (i + 1)) * mWidth * mPixelFormat));
}
/* read pixel data using row pointers */
png_read_image (png_ptr, row_pointers);
/* finish decompression and release memory */
png_read_end (png_ptr, NULL);
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
fclose (fp);
/* we don't need row pointers anymore */
delete[] row_pointers;
return 1;
}
Texture::Texture(std::string filename) : mPixels(NULL), mMipMapLevels(NULL), hasmipmap(false), mSu(1.f), mSv(1.f), clamp(false){
if (!load(filename)) {
std::cerr << "Erreur : impossible de lire le fichier " << filename << std::endl;
} else {
mlodmax = (int)std::floor(std::min(std::log2(mWidth), std::log2(mHeight)));
}
}
bool Texture::prefilter(){
hasmipmap = buildmipmaps();
return hasmipmap;
}
bool Texture::buildmipmaps(){
mMipMapLevels = new unsigned char*[mlodmax+1];
mMipMapLevels[0] = mPixels;
for (int i=1;i<mlodmax+1; ++i)
mMipMapLevels[i]=NULL;
// Call of the student function
return prefilterTexture(mMipMapLevels, mWidth, mHeight, mPixelFormat, mlodmax+1);
}
void Texture::setScale(float su, float sv){
mSu = 1.f/su;
mSv = 1.f/sv;
}
void Texture::getPixel(float u, float v, float lod, Color &color){
if (clamp){
u = (u>0) ? ((u<1) ? u : 1) : 0;
v = (v>0) ? ((v<1) ? v : 1) : 0;
}
u = mSu*u;
v = mSv*v;
float scaledU = u * (mWidth - 1);
float scaledV = v * (mHeight - 1);
// color = Color(u,v,0);
// return;
int s = ((int)scaledU) % mWidth;
int t = ((int)scaledV) % mHeight;
float ds = scaledU - std::floor(scaledU);
float dt = scaledV - std::floor(scaledV);
if (s<0){
s = s+mWidth;
}
if (t<0){
t = t+mHeight;
}
// Nearest
// color = interpolate(mPixels, mWidth, mHeight, s,t, 0.f,0.f);
// return;
// Linear
// color = interpolate(mPixels, mWidth, mHeight, s,t, ds, dt);
// return;
/* Filtrage */
lod = std::min(lod-1, (float)mlodmax);
if (lod > 0) {
// color = Color(0, std::floor(lod), 0);
// return;
int baselod = (int)std::floor(lod);
int pix = (int)(std::pow(2.f,baselod)); // bigger inferior mipmap level
if (hasmipmap){
// Sous-echantillonnage --> filtrage mip-map
Color color1 = integrateMipMap(baselod, ((float)s + ds)/pix, ((float)t + dt)/pix, mWidth/pix, mHeight/pix);
if (baselod < mlodmax){
pix = std::pow(2.f,baselod+1); // smaller superior mipmap level
Color color2 = integrateMipMap(baselod+1, ((float)s + ds)/pix, ((float)t + dt)/pix, mWidth/pix, mHeight/pix);
float dm = lod - std::floor(lod);
color = color1 * (1-dm) + color2 * dm;
} else {
color = color1;
}
} else {
int cs = pix * (s/pix);
int ct = pix * (t/pix);
Color color1 = integrateExplicit(mMipMapLevels[0], mWidth, mHeight, mPixelFormat, cs, ct, pix, pix);
if (baselod < mlodmax){
pix = std::pow(2.f,baselod+1); // smaller superior mipmap level
cs = pix * (s/pix);
ct = pix * (t/pix);
Color color2 = integrateExplicit(mMipMapLevels[0], mWidth, mHeight, mPixelFormat, cs, ct, pix, pix);
float dm = lod - std::floor(lod);
color = color1 * (1-dm) + color2 * dm;
} else {
color = color1;
}
}
} else {
// color = Color(1/*fabsf(lod)*/, 0, 0);
// return;
color = interpolate(mPixels, mWidth, mHeight, s,t, ds,dt);
}
}
Color Texture::integrateMipMap(int level, float s, float t, int w, int h){
Color color(0.f, 0.f, 0.f);
color = interpolate(mMipMapLevels[level], w, h, (int)s, (int)t, s-std::floor(s), t-std::floor(t));
return color;
}
Color Texture::integrateExplicit(unsigned char *pixels, int imageWidth, int imageHeight, int pixelFormat, float s, float t, int w, int h){
#if 0
Color color(0.f, 0.f, 0.f);
for (int i=0; i<h; i++)
for (int j=0; j<w; j++) {
int k = ((int)(t+i)) % imageHeight;
int l = ((int)(s+j)) % imageWidth;
int linPos = (k*imageWidth + l)*(pixelFormat);
Color centerColor(((float) pixels[linPos])/255.0, ((float) pixels[linPos+1])/255.0, ((float) pixels[linPos+2])/255.0);
color = color + centerColor;
}
color = color * (1.f/(w*h));
return color;
#endif
// Call of the student function
return integrateTexture(pixels, imageWidth, imageHeight, pixelFormat, s, t, w, h);
}
Color Texture::interpolate(unsigned char *pixels, int w, int h, int s, int t, float ds, float dt){
#if 0
Color color;
// Sur échantillonnage --> interpolation
// nearest texel
int linPos = (t*w + s)*(mPixelFormat);
Color centerColor(((float) pixels[linPos])/255.0, ((float) pixels[linPos+1])/255.0, ((float) pixels[linPos+2])/255.0);
Color rightColor;
int r = std::min(s+1, w-1);
int posds = (t*w + r)*(mPixelFormat);
rightColor = Color(
((float) pixels[posds])/255.0,
((float) pixels[posds+1])/255.0,
((float) pixels[posds+2])/255.0
);
Color upColor;
int p = std::min(t+1, h-1);
int posdt = (p*w + s)*(mPixelFormat);
upColor = Color (
((float) pixels[posdt])/255.0,
((float) pixels[posdt+1])/255.0,
((float) pixels[posdt+2])/255.0
);
Color upRightColor;
int posdtds = (p*w + r)*(mPixelFormat);
upRightColor = Color (
((float) pixels[posdtds])/255.0,
((float) pixels[posdtds+1])/255.0,
((float) pixels[posdtds+2])/255.0
);
rightColor = centerColor*(1-ds) + rightColor*ds;
upColor = upColor*(1-ds) + upRightColor*ds;
color = rightColor*(1-dt) + upColor*dt;
return color;
#endif
// Call of the student function
return interpolateTexture(pixels, w, h, mPixelFormat, (float)s+ds, (float)t+dt);
}
And here's errors in terminal:
In file included from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.cpp:1:0:
/home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.hpp: In member function ‘float Texture::computeMipmapLevel(const Vector3D&, const Vector3D&)’:
/home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.hpp:55:16: error: ‘log2’ is not a member of ‘std’
return std::log2(std::max(dTdx.norm()*width()*mSu, dTdy.norm()*height()*mSv));
^
/home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.hpp:55:16: note: suggested alternative:
In file included from /usr/include/features.h:374:0,
from /usr/include/x86_64-linux-gnu/c++/4.8/bits/os_defines.h:39,
from /usr/include/x86_64-linux-gnu/c++/4.8/bits/c++config.h:426,
from /usr/include/c++/4.8/cmath:41,
from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/vector3d.hpp:3,
from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.hpp:4,
from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.cpp:1:
/usr/include/x86_64-linux-gnu/bits/math-finite.h:344:15: note: ‘log2’
extern double __REDIRECT_NTH (log2, (double), __log2_finite);
^
/home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.cpp: In constructor ‘Texture::Texture(std::string)’:
/home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.cpp:162:44: error: ‘log2’ is not a member of ‘std’
mlodmax = (int)std::floor(std::min(std::log2(mWidth), std::log2(mHeight)));
^
/home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.cpp:162:44: note: suggested alternative:
In file included from /usr/include/features.h:374:0,
from /usr/include/x86_64-linux-gnu/c++/4.8/bits/os_defines.h:39,
from /usr/include/x86_64-linux-gnu/c++/4.8/bits/c++config.h:426,
from /usr/include/c++/4.8/cmath:41,
from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/vector3d.hpp:3,
from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.hpp:4,
from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.cpp:1:
/usr/include/x86_64-linux-gnu/bits/math-finite.h:344:15: note: ‘log2’
extern double __REDIRECT_NTH (log2, (double), __log2_finite);
^
/home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.cpp:162:63: error: ‘log2’ is not a member of ‘std’
mlodmax = (int)std::floor(std::min(std::log2(mWidth), std::log2(mHeight)));
^
/home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.cpp:162:63: note: suggested alternative:
In file included from /usr/include/features.h:374:0,
from /usr/include/x86_64-linux-gnu/c++/4.8/bits/os_defines.h:39,
from /usr/include/x86_64-linux-gnu/c++/4.8/bits/c++config.h:426,
from /usr/include/c++/4.8/cmath:41,
from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/vector3d.hpp:3,
from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.hpp:4,
from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.cpp:1:
/usr/include/x86_64-linux-gnu/bits/math-finite.h:344:15: note: ‘log2’
extern double __REDIRECT_NTH (log2, (double), __log2_finite);
I took a look on internet and I tried to include and but it's still not working...
Thanks!
log2 is a C99 function. It's not in C++98, and therefore not in the std namespace. Either compile in C++11 mode (C++11 is aligned with C99):
// g++ -std=c++11
#include <cmath>
#include <iostream>
int main()
{
std::cout << std::log2(3) << std::endl;
}
... or get the function from the C header, without the namespace:
// g++ -std=c++98
#include <iostream>
#include <math.h>
int main()
{
std::cout << log2(3) << std::endl;
}
I have use libpng to generate png file. for RGB png, there is no problem. but I want to give the png a transparent property.
I reference some code, and add the places where use * to flaged
*row++ = 230;
*PNG_COLOR_TYPE_RGBA,*
to make it have transparent property,
the code is run sucessfully, but I did't see the transparent effect.
can anyone familiar with libpng or png operation help me?
In this code,
where the pixel_t,& bitmap_t is some data with rgb data.
/* A colored pixel. */
typedef struct {
uint8_t red;
uint8_t green;
uint8_t blue;
} pixel_t;
/* A picture. */
typedef struct {
pixel_t *pixels;
size_t width;
size_t height;
} bitmap_t;
static int save_png_to_file (bitmap_t *bitmap, const char *path)
{
FILE * fp;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
size_t x, y;
png_byte ** row_pointers = NULL;
/* "status" contains the return value of this function. At first
it is set to a value which means 'failure'. When the routine
has finished its work, it is set to a value which means
'success'. */
int status = -1;
/* The following number is set by trial and error only. I cannot
see where it it is documented in the libpng manual.
*/
int pixel_size = 3;
int depth = 8;
fp = fopen (path, "wb");
if (! fp) {
goto fopen_failed;
}
png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
goto png_create_write_struct_failed;
}
info_ptr = png_create_info_struct (png_ptr);
if (info_ptr == NULL) {
goto png_create_info_struct_failed;
}
// png_set_invert_alpha(png_ptr);
/* Set up error handling. */
if (setjmp (png_jmpbuf (png_ptr))) {
goto png_failure;
}
/* Set image attributes. */
png_set_IHDR (png_ptr,
info_ptr,
bitmap->width,
bitmap->height,
depth,
*PNG_COLOR_TYPE_RGBA,*
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
/* Initialize rows of PNG. */
//(png_byte **) added by li
row_pointers = (png_byte **)png_malloc (png_ptr, bitmap->height * sizeof (png_byte *));
for (y = 0; y < bitmap->height; ++y) {
//png_byte * added by li
// png_byte *row = (png_byte *)
// png_malloc (png_ptr, sizeof (uint8_t) * bitmap->width * pixel_size);
png_byte *row = (png_byte *)
png_malloc (png_ptr, sizeof (uint8_t) * bitmap->width * 4);
row_pointers[y] = row;
for (x = 0; x < bitmap->width; ++x) {
pixel_t * pixel = pixel_at (bitmap, x, y);
*row++ = pixel->red;
*row++ = pixel->green;
*row++ = pixel->blue;
**row++ = 230;*
}
}
/* Write the image data to "fp". */
png_init_io (png_ptr, fp);
png_set_rows (png_ptr, info_ptr, row_pointers);
png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
/* The routine has successfully written the file, so we set
"status" to a value which indicates success. */
status = 0;
for (y = 0; y < bitmap->height; y++) {
png_free (png_ptr, row_pointers[y]);
}
png_free (png_ptr, row_pointers);
png_failure:
png_create_info_struct_failed:
png_destroy_write_struct (&png_ptr, &info_ptr);
png_create_write_struct_failed:
fclose (fp);
fopen_failed:
return status;
}
A truecolor PNG image with alpha , with bitdepth=8, stores the transparency as an extra channel, in RGBA order, in the range 0-255 (0=fully transparent; 255=fully opaque).
What you are doing looks correct to me. Only that a value of 230 means "almost opaque", it might be difficult to detect visually the trasnparency. Try with other values.
BTW, bear in mind that there are other ways to add transparency to a PNG image, see my answer here.
Since this question seems to be the top answer for 'libpng add transparency', I'm adding a compile ready source to this answer
// gcc -g -Wall -o png2 png2.c -lpng
#include <stdio.h>
#include <stdlib.h>
#include <png.h>
#define uint8_t unsigned char
typedef struct {
uint8_t red;
uint8_t green;
uint8_t blue;
} pixel_t;
typedef struct {
pixel_t *pixels;
size_t width;
size_t height;
} bitmap_t;
pixel_t *pixel_at(bitmap_t *bitmap, int x, int y)
{return &bitmap->pixels[(bitmap->width * y) + x];}
static int save_png_to_file (bitmap_t *bitmap, const char *path)
{
FILE * fp;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
size_t x, y;
png_byte ** row_pointers = NULL;
pixel_t *pixel;
int status = -1;
int depth = 8;
if((fp = fopen(path, "wb")) == NULL)
goto fopen_failed;
if((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) == NULL)
goto png_create_write_struct_failed;
if((info_ptr = png_create_info_struct (png_ptr)) == NULL)
goto png_create_info_struct_failed;
if(setjmp (png_jmpbuf (png_ptr)))
goto png_failure;
png_set_IHDR (png_ptr,
info_ptr,
bitmap->width,
bitmap->height,
depth,
PNG_COLOR_TYPE_RGBA,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
row_pointers = (png_byte **)png_malloc (png_ptr, bitmap->height * sizeof (png_byte *));
for(y = 0; y < bitmap->height; y++)
{
png_byte *row = (png_byte *)
png_malloc (png_ptr, sizeof (uint8_t) * bitmap->width * 4);
row_pointers[y] = row;
for (x = 0; x < bitmap->width; ++x)
{
pixel = pixel_at (bitmap, x, y);
*row++ = pixel->red;
*row++ = pixel->green;
*row++ = pixel->blue;
*row++ = x; // 0 - completely transparent 255 - completely opaque
}
}
png_init_io (png_ptr, fp);
png_set_rows (png_ptr, info_ptr, row_pointers);
png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
status = 0;
for(y = 0; y < bitmap->height; y++)
png_free (png_ptr, row_pointers[y]);
png_free (png_ptr, row_pointers);
png_failure:
png_create_info_struct_failed:
png_destroy_write_struct (&png_ptr, &info_ptr);
png_create_write_struct_failed:
fclose (fp);
fopen_failed:
return status;
}
int main(void)
{
bitmap_t pic;
unsigned char pixels[255*255*3];
int t;
pic.width = 255;
pic.height = 255;
for(t=0; t<255 * 255 * 3; t=t+3) // Just a red square
{
pixels[t+0] = 255;
pixels[t+1] = 0;
pixels[t+2] = 0;
}
pic.pixels = (pixel_t *) &pixels;
save_png_to_file(&pic, "test.png");
return 0;
}