v4l2 simple application - performance problem - c

I have a very simple setup where a camera connected to a Pi Zero is triggered by an external signal.
Problem: Achievable framerate is <0.4fps with my code using v4l2. Works fine with e.g.: raspivid.
With raspivid 1920x1080 # 50fps works as expected (tested using -pts to save timecodes). My code instead will never dequeue a buffer at 50Hz. I have to reduce the trigger rate to 5Hz before receiving frames every ~600ms! This looks like I'm still only receiving every third frame. The returned framebuffers always skip one index:
new frame 0: 3110400
time diff 0.6006
new frame 2: 3110400
time diff 0.6006
new frame 4: 3110400
time diff 0.600601
new frame 6: 3110400
time diff 0.6006
You can find my code below (exposure setup,... removed)
Question: Can you give me a hint what could be the problem? The program does nothing but dequeue and enqueue the buffers so performance should not be an issue.
#define NB_BUFFER 10
int main(int argc, char *argv[])
{
int exposure = 50;
int rows = 1080;
int cols = 1920;
if(argc > 1){
exposure= atoi(argv[1]);
}
if(argc > 3){
cols=atoi(argv[2]);
rows=atoi(argv[3]);
}
struct vdIn vdin;
struct vdIn *vd = &vdin;
if((vd->fd = open("/dev/video0", O_RDWR)) < 0){
perror("open");
exit(1);
}
if(ioctl(vd->fd, VIDIOC_QUERYCAP, &vd->cap) < 0){
perror("VIDIOC_QUERYCAP");
exit(1);
}
if(!(vd->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)){
fprintf(stderr, "The device does not handle single-planar video capture.\n");
exit(1);
}
vd->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vd->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;//V4L2_PIX_FMT_BGR24;
vd->fmt.fmt.pix.width = cols;
vd->fmt.fmt.pix.height = rows;
if(ioctl(vd->fd, VIDIOC_S_FMT, &vd->fmt) < 0){
perror("VIDIOC_S_FMT");
exit(1);
}
struct v4l2_control control;
control.id = V4L2_CID_EXPOSURE_AUTO ;
control.value = V4L2_EXPOSURE_MANUAL;
if(ioctl(vd->fd, VIDIOC_S_CTRL, &control) < 0){
perror("VIDIOC_S_CTRL EXPOSURE MANUAL");
exit(1);
}
.... iso manual, line frequency off, set exposure, auto whitebalance off
vd->rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vd->rb.memory = V4L2_MEMORY_MMAP;
vd->rb.count = NB_BUFFER;
if(ioctl(vd->fd, VIDIOC_REQBUFS, &vd->rb) < 0){
perror("VIDIOC_REQBUFS");
exit(1);
}
int ret;
/* map the buffers */
struct v4l2_buffer buf;
for (int i = 0; i < NB_BUFFER; i++) {
memset (&vd->buf, 0, sizeof (struct v4l2_buffer));
vd->buf.index = i;
vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vd->buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl (vd->fd, VIDIOC_QUERYBUF, &vd->buf);
if (ret < 0) {
fprintf (stderr, "Unable to query buffer (%d).\n", errno);
return -1;
}
std::cout << "buf len " << vd->buf.length<<std::endl;
vd->mem[i] = mmap (0 /* start anywhere */ ,
vd->buf.length, PROT_READ, MAP_SHARED, vd->fd,
vd->buf.m.offset);
if (vd->mem[i] == MAP_FAILED) {
fprintf (stderr, "Unable to map buffer (%d)\n", errno);
return -1;
}
}
/* Queue the buffers. */
for (int i = 0; i < NB_BUFFER; ++i) {
memset (&vd->buf, 0, sizeof (struct v4l2_buffer));
vd->buf.index = i;
vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vd->buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl (vd->fd, VIDIOC_QBUF, &vd->buf);
if (ret < 0) {
fprintf (stderr, "Unable to queue buffer (%d).\n", errno);
return -1;
}
}
// Activate streaming
int type = vd->fmt.type;
if(ioctl(vd->fd, VIDIOC_STREAMON, &type) < 0){
perror("VIDIOC_STREAMON");
exit(1);
}
bool capture_is_running = true;
double lastTimestamp=0;
while(capture_is_running){
memset (&vd->buf, 0, sizeof (struct v4l2_buffer));
vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vd->buf.memory = V4L2_MEMORY_MMAP;
std::cout << "try to dequeue" << std::endl;
ret = ioctl (vd->fd, VIDIOC_DQBUF, &vd->buf);
if (ret < 0) {
fprintf (stderr, "Unable to dequeue buffer (%d).\n", errno);
return -1;
}
cout << "new frame "<<vd->buf.index<<": "<< vd->buf.bytesused << endl;
double timestamp = vd->buf.timestamp.tv_sec + vd->buf.timestamp.tv_usec/1000000.;
double timeDiff = timestamp - lastTimestamp;
lastTimestamp = timestamp;
std::cout << "time diff " << timeDiff << std::endl;
ret = ioctl (vd->fd, VIDIOC_QBUF, &vd->buf);
if (ret < 0) {
fprintf (stderr, "Unable to requeue buffer (%d).\n", errno);
return -1;
}
}
// Deactivate streaming
if(ioctl(vd->fd, VIDIOC_STREAMOFF, &type) < 0){
perror("VIDIOC_STREAMOFF");
exit(1);
}
close(vd->fd);
return EXIT_SUCCESS;
return 0;
}

Related

Video4linux: Video encoder driver never delivers encoded frames

I have an SoM based on the Qualcomm Snapdragon 410, which has a hardware video encoder which is exposed as a Video4Linux device. I have some C code which uses the device according to the M2M encoder interface (https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/dev-encoder.html). However, I only get one encoded frame on the CAPTURE side, even though I'm pushing a lot of frames to the OUTPUT side.
Here's some annotated source code which, to my knowledge, should follow the M2M encoder interface correctly: https://gist.github.com/mortie/61d6d269e523639a204ffb052a47a516. The output it produces is at the bottom of the file. You can see that only one CAPTURE buffer is dequeued, even though I'm constantly enqueuing and dequeuing OUTPUT buffers.
Here's my C source code, since that has to be inline in the post:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <linux/videodev2.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/eventfd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <poll.h>
#define xioctl(a, b, c) do { \
if (ioctl(a, b, c) < 0) { \
fprintf(stderr, "%s:%i: IOCTL %s: %s\n", __FILE__, __LINE__, #b, strerror(errno)); \
abort(); \
} \
} while (0)
struct mmbuffer {
void *start;
size_t length;
int ready;
};
int main() {
int width = 640;
int height = 480;
int fd = -1;
struct v4l2_capability cap;
for (int id = 0; id < 16; ++id) {
char pathbuf[64];
snprintf(pathbuf, sizeof(pathbuf), "/dev/video%d", id);
int tfd = open(pathbuf, O_RDWR);
if (tfd < 0) {
continue;
}
memset(&cap, 0, sizeof(cap));
if (ioctl(tfd, VIDIOC_QUERYCAP, &cap) < 0) {
close(tfd);
continue;
}
if (strcmp((const char *)cap.card, "Qualcomm Venus video encoder") == 0) {
fprintf(stderr, "Found %s (%s, fd %i)\n", cap.card, pathbuf, tfd);
fd = tfd;
break;
}
}
if (fd < 0) {
fprintf(stderr, "Found no encoder\n");
return 1;
}
// 1. Set the coded format on the CAPTURE queue via VIDIOC_S_FMT().
struct v4l2_format fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
fmt.fmt.pix_mp.num_planes = 1;
fmt.fmt.pix_mp.width = width;
fmt.fmt.pix_mp.height = height;
fmt.fmt.pix_mp.plane_fmt[0].sizeimage = 1024 * 1024;
xioctl(fd, VIDIOC_S_FMT, &fmt);
// 2. Optional. Enumerate supported OUTPUT formats (raw formats for source) for the selected
// coded format via VIDIOC_ENUM_FMT().
struct v4l2_fmtdesc fmtdesc;
memset(&fmtdesc, 0, sizeof(fmtdesc));
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
while (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) == 0) {
char fcc[4];
memcpy(fcc, &fmtdesc.pixelformat, 4);
fprintf(stderr, "Output format %i: %c%c%c%c: %s\n", fmtdesc.index, fcc[0], fcc[1], fcc[2], fcc[3], fmtdesc.description);
fmtdesc.index += 1;
}
// Let's do the same with CAPTURE
memset(&fmtdesc, 0, sizeof(fmtdesc));
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
while (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) == 0) {
char fcc[4];
memcpy(fcc, &fmtdesc.pixelformat, 4);
fprintf(stderr, "Capture format %i: %c%c%c%c: %s\n", fmtdesc.index, fcc[0], fcc[1], fcc[2], fcc[3], fmtdesc.description);
fmtdesc.index += 1;
}
// 3. Set the raw source format on the OUTPUT queue via VIDIOC_S_FMT().
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12;
fmt.fmt.pix_mp.width = width;
fmt.fmt.pix_mp.height = height;
xioctl(fd, VIDIOC_S_FMT, &fmt);
// 4. Set the raw frame interval on the OUTPUT queue via VIDIOC_S_PARM(). This also sets the
// coded frame interval on the CAPTURE queue to the same value.
struct v4l2_streamparm parm;
memset(&parm, 0, sizeof(parm));
parm.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
parm.parm.output.timeperframe.numerator = 1;
parm.parm.output.timeperframe.denominator = 30;
xioctl(fd, VIDIOC_S_PARM, &parm);
// 5. Optional. Set the coded frame interval on the CAPTURE queue via VIDIOC_S_PARM().
memset(&parm, 0, sizeof(parm));
parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
parm.parm.capture.timeperframe.numerator = 1;
parm.parm.capture.timeperframe.denominator = 30;
xioctl(fd, VIDIOC_S_PARM, &parm);
// 6. Optional. Set the visible resolution for the stream metadata via VIDIOC_S_SELECTION() on
// the OUTPUT queue if it is desired to be different than the full OUTPUT resolution.
// 7. Allocate buffers for both OUTPUT and CAPTURE via VIDIOC_REQBUFS().
// This may be performed in any order.
struct mmbuffer *captureBufs = NULL;
size_t captureBufCount;
{ // CAPTURE
struct v4l2_requestbuffers reqbufs;
memset(&reqbufs, 0, sizeof(reqbufs));
reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
reqbufs.memory = V4L2_MEMORY_MMAP;
reqbufs.count = 4;
xioctl(fd, VIDIOC_REQBUFS, &reqbufs);
captureBufCount = reqbufs.count;
captureBufs = (struct mmbuffer *)malloc(captureBufCount * sizeof(*captureBufs));
for (size_t i = 0; i < captureBufCount; ++i) {
struct v4l2_buffer buffer;
memset(&buffer, 0, sizeof(buffer));
struct v4l2_plane plane;
memset(&plane, 0, sizeof(plane));
buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
buffer.length = 1;
buffer.m.planes = &plane;
buffer.memory = V4L2_MEMORY_MMAP;
buffer.index = i;
xioctl(fd, VIDIOC_QUERYBUF, &buffer);
captureBufs[i].ready = 1;
captureBufs[i].start = mmap(NULL, plane.length, PROT_READ | PROT_WRITE, MAP_SHARED,
fd, plane.m.mem_offset);
captureBufs[i].length = plane.length;
if (captureBufs[i].start == MAP_FAILED) {
fprintf(stderr, "mmap: %s\n", strerror(errno));
return 1;
}
fprintf(stderr, "Mapped buffer %zi: %p, %i\n", i, captureBufs[i].start, plane.length);
}
}
struct mmbuffer *outputBufs = NULL;
size_t outputBufCount;
{ // OUTPUT
struct v4l2_requestbuffers reqbufs;
memset(&reqbufs, 0, sizeof(reqbufs));
reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
reqbufs.memory = V4L2_MEMORY_MMAP;
reqbufs.count = 4;
xioctl(fd, VIDIOC_REQBUFS, &reqbufs);
outputBufCount = reqbufs.count;
outputBufs = (struct mmbuffer *)malloc(outputBufCount * sizeof(*captureBufs));
for (size_t i = 0; i < outputBufCount; ++i) {
struct v4l2_buffer buffer;
memset(&buffer, 0, sizeof(buffer));
struct v4l2_plane plane;
memset(&plane, 0, sizeof(plane));
buffer.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
buffer.length = 1;
buffer.m.planes = &plane;
buffer.memory = V4L2_MEMORY_MMAP;
buffer.index = i;
xioctl(fd, VIDIOC_QUERYBUF, &buffer);
outputBufs[i].ready = 1;
outputBufs[i].start = mmap(NULL, plane.length, PROT_READ | PROT_WRITE, MAP_SHARED,
fd, plane.m.mem_offset);
outputBufs[i].length = plane.length;
if (outputBufs[i].start == MAP_FAILED) {
fprintf(stderr, "mmap: %s\n", strerror(errno));
return 1;
}
fprintf(stderr, "Mapped buffer %zi: %p, %i\n", i, outputBufs[i].start, plane.length);
}
}
// 8. Begin streaming on both OUTPUT and CAPTURE queues via VIDIOC_STREAMON().
// This may be performed in any order.
int buftype = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
xioctl(fd, VIDIOC_STREAMON, &buftype);
buftype = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
xioctl(fd, VIDIOC_STREAMON, &buftype);
// Then enqueue all the capture buffers, to let the driver put encoded frames in them
for (size_t i = 0; i < captureBufCount; ++i) {
struct v4l2_buffer buffer;
memset(&buffer, 0, sizeof(buffer));
struct v4l2_plane plane;
memset(&plane, 0, sizeof(plane));
buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
buffer.index = i;
buffer.length = 1;
buffer.m.planes = &plane;
buffer.memory = V4L2_MEMORY_MMAP;
xioctl(fd, VIDIOC_QBUF, &buffer);
}
// This is the main loop, where we dequeue and re-enqueue available CAPTURE buffers,
// dequeue available OUTPUT buffers, and write frames to the OUTPUT
uint8_t fill = 0;
while (1) {
// Handle events from the driver
struct pollfd pfd = {fd, POLLIN | POLLOUT, 0};
while (1) {
int ret = poll(&pfd, 1, 0);
if (ret < 0 && errno == EINTR) {
continue;
} else if (ret < 0) {
fprintf(stderr, "Poll error: %s\n", strerror(errno));
return 1;
} else if (ret == 0) {
break;
}
if (pfd.revents & POLLIN) {
// A capture buffer is ready, we have encoded data!
struct v4l2_buffer buffer;
memset(&buffer, 0, sizeof(buffer));
struct v4l2_plane plane;
memset(&plane, 0, sizeof(plane));
buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
buffer.memory = V4L2_MEMORY_MMAP;
buffer.length = 1;
buffer.m.planes = &plane;
xioctl(fd, VIDIOC_DQBUF, &buffer);
// Do something with the data
struct mmbuffer *buf = &captureBufs[buffer.index];
fprintf(stderr, "Capture buffer %i dequeued (at: %p, length: %i)\n", buffer.index, buf->start, plane.bytesused);
// Re-enqueue the buffer
size_t index = buffer.index;
memset(&buffer, 0, sizeof(buffer));
memset(&plane, 0, sizeof(plane));
buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
buffer.memory = V4L2_MEMORY_MMAP;
buffer.length = 1;
buffer.m.planes = &plane;
buffer.index = index;
xioctl(fd, VIDIOC_QBUF, &buffer);
fprintf(stderr, "Capture buffer %i enqueued\n", buffer.index);
}
if (pfd.revents & POLLOUT) {
// An output buffer is ready, dequeue it and mark it ready
struct v4l2_buffer buffer;
memset(&buffer, 0, sizeof(buffer));
struct v4l2_plane plane;
memset(&plane, 0, sizeof(plane));
buffer.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
buffer.memory = V4L2_MEMORY_MMAP;
buffer.length = 1;
buffer.m.planes = &plane;
if (ioctl(fd, VIDIOC_DQBUF, &buffer) < 0) {
fprintf(stderr, "VIDIOC_DQBUF (output): %s\n", strerror(errno));
return 1;
}
fprintf(stderr, "Output buffer %i dequeued, marking ready\n", buffer.index);
outputBufs[buffer.index].ready = 1;
}
if (pfd.revents & ~(POLLIN | POLLOUT)) {
fprintf(stderr, "Unexpected revents: %i. Error?\n", pfd.revents);
return 1;
}
}
// Find an available output buffer
int outputIdx = -1;
struct mmbuffer *outputBuf = NULL;
for (size_t i = 0; i < outputBufCount; ++i) {
if (outputBufs[i].ready) {
outputIdx = i;
outputBuf = &outputBufs[i];
break;
}
}
// Produce a raw frame and queue it, if possible
if (outputBuf) {
size_t len = width * height + width * height / 2;
memset(outputBuf->start, fill, len);
fill += 1;
struct v4l2_buffer buffer;
memset(&buffer, 0, sizeof(buffer));
struct v4l2_plane plane;
memset(&plane, 0, sizeof(plane));
buffer.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
buffer.length = 1;
buffer.m.planes = &plane;
buffer.memory = V4L2_MEMORY_MMAP;
buffer.index = outputIdx;
plane.bytesused = len;
if (ioctl(fd, VIDIOC_QBUF, &buffer) < 0) {
fprintf(stderr, "VIDIOC_QBUF (output): %s\n", strerror(errno));
return 1;
}
fprintf(stderr, "Output buffer %i enqueued, marking not ready\n", buffer.index);
outputBufs[outputIdx].ready = 0;
} else {
fprintf(stderr, "No output buffers ready!\n");
}
usleep(33 * 1000);
}
}
And here's the output, where you can see that POLLIN never triggers after the first time:
Found Qualcomm Venus video encoder (/dev/video5, fd 8)
Output format 0: NV12: Y/CbCr 4:2:0
Capture format 0: MPG4: MPEG-4 Part 2 ES
Capture format 1: H263: H.263
Capture format 2: H264: H.264
Capture format 3: VP80: VP8
Mapped buffer 0: 0xffffb40d0000, 1048576
Mapped buffer 1: 0xffffb3fd0000, 1048576
Mapped buffer 2: 0xffffb3ed0000, 1048576
Mapped buffer 3: 0xffffb3dd0000, 1048576
Mapped buffer 0: 0xffffb3d5c000, 475136
Mapped buffer 1: 0xffffb3ce8000, 475136
Mapped buffer 2: 0xffffb3c74000, 475136
Mapped buffer 3: 0xffffb3c00000, 475136
Mapped buffer 4: 0xffffb3b8c000, 475136
Output buffer 0 enqueued, marking not ready
Capture buffer 0 dequeued (at: 0xffffb40d0000, length: 1264)
Capture buffer 0 enqueued
Output buffer 0 dequeued, marking ready
Output buffer 0 enqueued, marking not ready
Output buffer 0 dequeued, marking ready
Output buffer 0 enqueued, marking not ready
Output buffer 0 dequeued, marking ready
Output buffer 0 enqueued, marking not ready
Output buffer 0 dequeued, marking ready
Output buffer 0 enqueued, marking not ready
Output buffer 0 dequeued, marking ready

FFMPEG libx265 encoding leaves memory unfreed after avcodec_free_context

I am working on H265 encoding software and, in my unit tests, I have some weird memory leaks. To found them, I have modified the encode_video.c example from FFMPEG documentation. I have changed the resolution to correspond at a 4K video, I have adapted the bitrate and I have added a pause before context allocation and another one before the final return :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
static void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,
FILE *outfile)
{
int ret;
/* send the frame to the encoder */
if (frame)
printf("Send frame %3"PRId64"\n", frame->pts);
ret = avcodec_send_frame(enc_ctx, frame);
if (ret < 0) {
fprintf(stderr, "Error sending a frame for encoding\n");
exit(1);
}
while (ret >= 0) {
ret = avcodec_receive_packet(enc_ctx, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) {
fprintf(stderr, "Error during encoding\n");
exit(1);
}
printf("Write packet %3"PRId64" (size=%5d)\n", pkt->pts, pkt->size);
fwrite(pkt->data, 1, pkt->size, outfile);
av_packet_unref(pkt);
}
}
int main(int argc, char **argv)
{
const char *filename, *codec_name;
const AVCodec *codec;
AVCodecContext *c= NULL;
int i, ret, x, y;
FILE *f;
AVFrame *frame;
AVPacket *pkt;
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
if (argc <= 2) {
fprintf(stderr, "Usage: %s <output file> <codec name>\n", argv[0]);
exit(0);
}
filename = argv[1];
codec_name = argv[2];
sleep(10);
/* find the mpeg1video encoder */
codec = avcodec_find_encoder_by_name(codec_name);
if (!codec) {
fprintf(stderr, "Codec '%s' not found\n", codec_name);
exit(1);
}
c = avcodec_alloc_context3(codec);
if (!c) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
pkt = av_packet_alloc();
if (!pkt)
exit(1);
/* put sample parameters */
c->bit_rate = 1000000;
/* resolution must be a multiple of two */
c->width = 3840;
c->height = 2160;
/* frames per second */
c->time_base = (AVRational){1, 25};
c->framerate = (AVRational){25, 1};
/* emit one intra frame every ten frames
* check frame pict_type before passing frame
* to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
* then gop_size is ignored and the output of encoder
* will always be I frame irrespective to gop_size
*/
c->gop_size = 10;
c->max_b_frames = 1;
c->pix_fmt = AV_PIX_FMT_YUV420P;
if (codec->id == AV_CODEC_ID_H264)
av_opt_set(c->priv_data, "preset", "slow", 0);
/* open it */
ret = avcodec_open2(c, codec, NULL);
if (ret < 0) {
fprintf(stderr, "Could not open codec: %s\n", av_err2str(ret));
exit(1);
}
f = fopen(filename, "wb");
if (!f) {
fprintf(stderr, "Could not open %s\n", filename);
exit(1);
}
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}
frame->format = c->pix_fmt;
frame->width = c->width;
frame->height = c->height;
ret = av_frame_get_buffer(frame, 0);
if (ret < 0) {
fprintf(stderr, "Could not allocate the video frame data\n");
exit(1);
}
/* encode 1 second of video */
for (i = 0; i < 25; i++) {
fflush(stdout);
/* make sure the frame data is writable */
ret = av_frame_make_writable(frame);
if (ret < 0)
exit(1);
/* prepare a dummy image */
/* Y */
for (y = 0; y < c->height; y++) {
for (x = 0; x < c->width; x++) {
frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;
}
}
/* Cb and Cr */
for (y = 0; y < c->height/2; y++) {
for (x = 0; x < c->width/2; x++) {
frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;
frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;
}
}
frame->pts = i;
/* encode the image */
encode(c, frame, pkt, f);
}
/* flush the encoder */
encode(c, NULL, pkt, f);
/* add sequence end code to have a real MPEG file */
if (codec->id == AV_CODEC_ID_MPEG1VIDEO || codec->id == AV_CODEC_ID_MPEG2VIDEO)
fwrite(endcode, 1, sizeof(endcode), f);
fclose(f);
avcodec_free_context(&c);
av_frame_free(&frame);
av_packet_free(&pkt);
sleep(10);
return 0;
}
I was expecting that the RAM memory usage at the first pause is the same as the second pause but there is about 55 Mo of difference. If I increase the number of encoded frames, this difference up to 390 Mo. I have tested this code under Linux Mint LMDE 4 (roughly same as Debian 10).
I guess this memory "leak" it isn't a real memory leak but that it's some internal values used by libx265 to be maybe reused for another encoding. But has there a way to free this memory through FFMPEG API?

How to properly set up ALSA device

Edit: This question is different than the proposed duplicate because I'm asking How do you set the period/buffer size that will work with multiple targets each with different sound hardware?.
I have created some code that attempts to set up ALSA before playback of an OGG file. The code below works on one embedded Linux platform, but on another it fails with the following output:
Error setting buffersize.
Playback open error: Operation not permitted
I've included only the code that demonstrates the issue. setup_alsa() is not complete and won't completely configure an alsa device.
#include <alsa/asoundlib.h>
char *buffer;
static char *device = "default";
snd_pcm_uframes_t periodsize = 8192; /* Periodsize (bytes) */
int setup_alsa(snd_pcm_t *handle)
{
int rc;
int dir = 0;
snd_pcm_uframes_t periods; /* Number of fragments/periods */
snd_pcm_hw_params_t *params;
snd_pcm_sw_params_t *sw_params;
int rate = 44100;
int exact_rate;
int i = 0;
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_alloca(&params);
/* Fill it in with default values. */
if (snd_pcm_hw_params_any(handle, params) < 0)
{
fprintf(stderr, "Can not configure this PCM device.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set number of periods. Periods used to be called fragments. */
periods = 4;
if ( snd_pcm_hw_params_set_periods(handle, params, periods, 0) < 0 )
{
fprintf(stderr, "Error setting periods.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set buffer size (in frames). The resulting latency is given by */
/* latency = periodsize * periods / (rate * bytes_per_frame) */
if (snd_pcm_hw_params_set_buffer_size(handle, params, (periodsize * periods)>>2) < 0)
{
fprintf(stderr, "Error setting buffersize.\n");
snd_pcm_close(handle);
return(-1);
}
/* Write the parameters to the driver */
rc = snd_pcm_hw_params(handle, params);
if (rc < 0)
{
fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
snd_pcm_close(handle);
return -1;
}
snd_pcm_hw_params_free(params);
What is the normal way to setup ALSA that doesn't require a specific buffer/period size be set that provides smooth audio playback?**
As it turns out, I can program my ALSA setup routine to let ALSA determine what the nearest working period/buffer size is by using snd_pcm_hw_params_set_buffer_size_near() instead of snd_pcm_hw_params_set_buffer_size().
The following code now works on both platforms:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <vorbis/vorbisfile.h>
#include <alsa/asoundlib.h>
char *buffer;
//static char *device = "default";
static char *device = "plughw:0,0";
snd_pcm_uframes_t periodsize = 4096; /* Periodsize (bytes) */
int setup_alsa(snd_pcm_t *handle)
{
int rc;
int dir = 0;
snd_pcm_uframes_t periods; /* Number of fragments/periods */
snd_pcm_hw_params_t *params;
snd_pcm_sw_params_t *sw_params;
int rate = 44100;
int exact_rate;
int i = 0;
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_malloc(&params);
/* Fill it in with default values. */
if (snd_pcm_hw_params_any(handle, params) < 0)
{
fprintf(stderr, "Can not configure this PCM device.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set the desired hardware parameters. */
/* Non-Interleaved mode */
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_NONINTERLEAVED);
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
/* 44100 bits/second sampling rate (CD quality) */
/* Set sample rate. If the exact rate is not supported */
/* by the hardware, use nearest possible rate. */
exact_rate = rate;
if (snd_pcm_hw_params_set_rate_near(handle, params, &exact_rate, 0) < 0)
{
fprintf(stderr, "Error setting rate.\n");
snd_pcm_close(handle);
return(-1);
}
if (rate != exact_rate)
{
fprintf(stderr, "The rate %d Hz is not supported by your hardware.\n==> Using %d Hz instead.\n", rate, exact_rate);
}
/* Set number of channels to 1 */
if( snd_pcm_hw_params_set_channels(handle, params, 1 ) < 0 )
{
fprintf(stderr, "Error setting channels.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set number of periods. Periods used to be called fragments. */
periods = 4;
if ( snd_pcm_hw_params_set_periods(handle, params, periods, 0) < 0 )
{
fprintf(stderr, "Error setting periods.\n");
snd_pcm_close(handle);
return(-1);
}
snd_pcm_uframes_t size = (periodsize * periods) >> 2;
if( (rc = snd_pcm_hw_params_set_buffer_size_near( handle, params, &size )) < 0)
{
fprintf(stderr, "Error setting buffersize: [%s]\n", snd_strerror(rc) );
snd_pcm_close(handle);
return(-1);
}
else
{
printf("Buffer size = %lu\n", (unsigned long)size);
}
/* Write the parameters to the driver */
rc = snd_pcm_hw_params(handle, params);
if (rc < 0)
{
fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
snd_pcm_close(handle);
return -1;
}
snd_pcm_hw_params_free(params);
/* Allocate a software parameters object. */
rc = snd_pcm_sw_params_malloc(&sw_params);
if( rc < 0 )
{
fprintf (stderr, "cannot allocate software parameters structure (%s)\n", snd_strerror(rc) );
return(-1);
}
rc = snd_pcm_sw_params_current(handle, sw_params);
if( rc < 0 )
{
fprintf (stderr, "cannot initialize software parameters structure (%s)\n", snd_strerror(rc) );
return(-1);
}
if((rc = snd_pcm_sw_params_set_avail_min(handle, sw_params, 1024)) < 0)
{
fprintf (stderr, "cannot set minimum available count (%s)\n", snd_strerror (rc));
return(-1);
}
rc = snd_pcm_sw_params_set_start_threshold(handle, sw_params, 1);
if( rc < 0 )
{
fprintf(stderr, "Error setting start threshold\n");
snd_pcm_close(handle);
return -1;
}
if((rc = snd_pcm_sw_params(handle, sw_params)) < 0)
{
fprintf (stderr, "cannot set software parameters (%s)\n", snd_strerror (rc));
return(-1);
}
snd_pcm_sw_params_free(sw_params);
return 0;
}
/* copied from libvorbis source */
int ov_fopen(const char *path, OggVorbis_File *vf)
{
int ret = 0;
FILE *f = fopen(path, "rb");
if( f )
{
ret = ov_open(f, vf, NULL, 0);
if( ret )
{
fclose(f);
}
}
else
{
ret = -1;
}
return( ret );
}
int main(int argc, char *argv[])
{
// sample rate * bytes per sample * channel count * seconds
//int bufferSize = 44100 * 2 * 1 * 2;
int err;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
buffer = (char *) malloc( periodsize );
if( buffer )
{
if((err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0)
{
printf("Playback open error #1: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if(err = setup_alsa(handle))
{
printf("Playback open error #2: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
OggVorbis_File vf;
int eof = 0;
int current_section;
err = ov_fopen(argv[1], &vf);
if(err != 0)
{
perror("Error opening file");
}
else
{
vorbis_info *vi = ov_info(&vf, -1);
fprintf(stderr, "Bitstream is %d channel, %ldHz\n", vi->channels, vi->rate);
fprintf(stderr, "Encoded by: %s\n\n", ov_comment(&vf, -1)->vendor);
while(!eof)
{
long ret = ov_read(&vf, buffer, periodsize, 0, 2, 1, &current_section);
if(ret == 0)
{
/* EOF */
eof = 1;
}
else if(ret < 0)
{
/* error in the stream. */
fprintf( stderr, "ov_read error %l", ret );
}
else
{
frames = snd_pcm_writen(handle, (void *)&buffer, ret/2);
if(frames < 0)
{
printf("snd_pcm_writen failed: %s\n", snd_strerror(frames));
if( frames == -EPIPE )
{
snd_pcm_prepare(handle);
//frames = snd_pcm_writen(handle, (void *)&buffer, ret/2);
}
else
{
break;
}
}
}
}
ov_clear(&vf);
}
free( buffer );
snd_pcm_drain(handle);
snd_pcm_close(handle);
}
return 0;
}

How to fix a segmentaion fault in a C program? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Segmentation fault
Currently I am upgrading an open source program used for HTTP streaming. It needs to support the latest FFMPEG.
The code compiles fine with no warnings although I am getting a segmentation fault error.
I would like to know how to fix the issue? and / or the best way to debug? Please find attached a portion of the code due to size. I will try to add the project to github :) Thanks in advance!
Sample Usage
# segmenter --i out.ts --l 10 --o stream.m3u8 --d segments --f stream
Makefile
FFLIBS=`pkg-config --libs libavformat libavcodec libavutil`
FFFLAGS=`pkg-config --cflags libavformat libavcodec libavutil`
all:
gcc -Wall -g segmenter.c -o segmenter ${FFFLAGS} ${FFLIBS}
segmenter.c
/*
* Copyright (c) 2009 Chase Douglas
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include <sys/stat.h>
#include "segmenter.h"
#include "libavformat/avformat.h"
#define IMAGE_ID3_SIZE 9171
void printUsage() {
fprintf(stderr, "\nExample: segmenter --i infile --d baseDir --f baseFileName --o playListFile.m3u8 --l 10 \n");
fprintf(stderr, "\nOptions: \n");
fprintf(stderr, "--i <infile>.\n");
fprintf(stderr, "--o <outfile>.\n");
fprintf(stderr, "--d basedir, the base directory for files.\n");
fprintf(stderr, "--f baseFileName, output files will be baseFileName-#.\n");
fprintf(stderr, "--l segment length, the length of each segment.\n");
fprintf(stderr, "--a, audio only decode for < 64k streams.\n");
fprintf(stderr, "--v, video only decode for < 64k streams.\n");
fprintf(stderr, "--version, print version details and exit.\n");
fprintf(stderr, "\n\n");
}
void ffmpeg_version() {
// output build and version numbers
fprintf(stderr, " libavutil version: %s\n", AV_STRINGIFY(LIBAVUTIL_VERSION));
fprintf(stderr, " libavutil build: %d\n", LIBAVUTIL_BUILD);
fprintf(stderr, " libavcodec version: %s\n", AV_STRINGIFY(LIBAVCODEC_VERSION));
fprintf(stdout, " libavcodec build: %d\n", LIBAVCODEC_BUILD);
fprintf(stderr, " libavformat version: %s\n", AV_STRINGIFY(LIBAVFORMAT_VERSION));
fprintf(stderr, " libavformat build: %d\n", LIBAVFORMAT_BUILD);
fprintf(stderr, " built on " __DATE__ " " __TIME__);
#ifdef __GNUC__
fprintf(stderr, ", gcc: " __VERSION__ "\n");
#else
fprintf(stderr, ", using a non-gcc compiler\n");
#endif
}
static AVStream *add_output_stream(AVFormatContext *output_format_context, AVStream *input_stream) {
AVCodecContext *input_codec_context;
AVCodecContext *output_codec_context;
AVStream *output_stream;
output_stream = avformat_new_stream(output_format_context, 0);
if (!output_stream) {
fprintf(stderr, "Segmenter error: Could not allocate stream\n");
exit(1);
}
input_codec_context = input_stream->codec;
output_codec_context = output_stream->codec;
output_codec_context->codec_id = input_codec_context->codec_id;
output_codec_context->codec_type = input_codec_context->codec_type;
output_codec_context->codec_tag = input_codec_context->codec_tag;
output_codec_context->bit_rate = input_codec_context->bit_rate;
output_codec_context->extradata = input_codec_context->extradata;
output_codec_context->extradata_size = input_codec_context->extradata_size;
if (av_q2d(input_codec_context->time_base) * input_codec_context->ticks_per_frame > av_q2d(input_stream->time_base) && av_q2d(input_stream->time_base) < 1.0 / 1000) {
output_codec_context->time_base = input_codec_context->time_base;
output_codec_context->time_base.num *= input_codec_context->ticks_per_frame;
} else {
output_codec_context->time_base = input_stream->time_base;
}
switch (input_codec_context->codec_type) {
#ifdef USE_OLD_FFMPEG
case CODEC_TYPE_AUDIO:
#else
case AVMEDIA_TYPE_AUDIO:
#endif
output_codec_context->channel_layout = input_codec_context->channel_layout;
output_codec_context->sample_rate = input_codec_context->sample_rate;
output_codec_context->channels = input_codec_context->channels;
output_codec_context->frame_size = input_codec_context->frame_size;
if ((input_codec_context->block_align == 1 && input_codec_context->codec_id == CODEC_ID_MP3) || input_codec_context->codec_id == CODEC_ID_AC3) {
output_codec_context->block_align = 0;
} else {
output_codec_context->block_align = input_codec_context->block_align;
}
break;
#ifdef USE_OLD_FFMPEG
case CODEC_TYPE_VIDEO:
#else
case AVMEDIA_TYPE_VIDEO:
#endif
output_codec_context->pix_fmt = input_codec_context->pix_fmt;
output_codec_context->width = input_codec_context->width;
output_codec_context->height = input_codec_context->height;
output_codec_context->has_b_frames = input_codec_context->has_b_frames;
if (output_format_context->oformat->flags & AVFMT_GLOBALHEADER) {
output_codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
break;
default:
break;
}
return output_stream;
}
int write_index_file(const char index[], const char tmp_index[], const unsigned int planned_segment_duration, const unsigned int actual_segment_duration[],
const char output_directory[], const char output_prefix[], const char output_file_extension[],
const unsigned int first_segment, const unsigned int last_segment) {
FILE *index_fp;
char *write_buf;
unsigned int i;
index_fp = fopen(tmp_index, "w");
if (!index_fp) {
fprintf(stderr, "Could not open temporary m3u8 index file (%s), no index file will be created\n", tmp_index);
return -1;
}
write_buf = malloc(sizeof (char) * 1024);
if (!write_buf) {
fprintf(stderr, "Could not allocate write buffer for index file, index file will be invalid\n");
fclose(index_fp);
return -1;
}
unsigned int maxDuration = planned_segment_duration;
for (i = first_segment; i <= last_segment; i++)
if (actual_segment_duration[i] > maxDuration)
maxDuration = actual_segment_duration[i];
snprintf(write_buf, 1024, "#EXTM3U\n#EXT-X-TARGETDURATION:%u\n", maxDuration);
if (fwrite(write_buf, strlen(write_buf), 1, index_fp) != 1) {
fprintf(stderr, "Could not write to m3u8 index file, will not continue writing to index file\n");
free(write_buf);
fclose(index_fp);
return -1;
}
for (i = first_segment; i <= last_segment; i++) {
snprintf(write_buf, 1024, "#EXTINF:%u,\n%s-%u%s\n", actual_segment_duration[i], output_prefix, i, output_file_extension);
if (fwrite(write_buf, strlen(write_buf), 1, index_fp) != 1) {
fprintf(stderr, "Could not write to m3u8 index file, will not continue writing to index file\n");
free(write_buf);
fclose(index_fp);
return -1;
}
}
snprintf(write_buf, 1024, "#EXT-X-ENDLIST\n");
if (fwrite(write_buf, strlen(write_buf), 1, index_fp) != 1) {
fprintf(stderr, "Could not write last file and endlist tag to m3u8 index file\n");
free(write_buf);
fclose(index_fp);
return -1;
}
free(write_buf);
fclose(index_fp);
return rename(tmp_index, index);
}
int main(int argc, const char *argv[]) {
//input parameters
char inputFilename[MAX_FILENAME_LENGTH], playlistFilename[MAX_FILENAME_LENGTH], baseDirName[MAX_FILENAME_LENGTH], baseFileName[MAX_FILENAME_LENGTH];
char baseFileExtension[5]; //either "ts", "aac" or "mp3"
int segmentLength, outputStreams, verbosity, version;
char currentOutputFileName[MAX_FILENAME_LENGTH];
char tempPlaylistName[MAX_FILENAME_LENGTH];
//these are used to determine the exact length of the current segment
double prev_segment_time = 0;
double segment_time;
unsigned int actual_segment_durations[2048];
double packet_time = 0;
//new variables to keep track of output size
double output_bytes = 0;
unsigned int output_index = 1;
AVOutputFormat *ofmt;
AVFormatContext *ic = NULL;
AVFormatContext *oc;
AVStream *video_st = NULL;
AVStream *audio_st = NULL;
AVCodec *codec;
int video_index;
int audio_index;
unsigned int first_segment = 1;
unsigned int last_segment = 0;
int write_index = 1;
int decode_done;
int ret;
int i;
unsigned char id3_tag[128];
unsigned char * image_id3_tag;
size_t id3_tag_size = 73;
int newFile = 1; //a boolean value to flag when a new file needs id3 tag info in it
if (parseCommandLine(inputFilename, playlistFilename, baseDirName, baseFileName, baseFileExtension, &outputStreams, &segmentLength, &verbosity, &version, argc, argv) != 0)
return 0;
if (version) {
ffmpeg_version();
return 0;
}
fprintf(stderr, "%s %s\n", playlistFilename, tempPlaylistName);
image_id3_tag = malloc(IMAGE_ID3_SIZE);
if (outputStreams == OUTPUT_STREAM_AUDIO)
build_image_id3_tag(image_id3_tag);
build_id3_tag((char *) id3_tag, id3_tag_size);
snprintf(tempPlaylistName, strlen(playlistFilename) + strlen(baseDirName) + 1, "%s%s", baseDirName, playlistFilename);
strncpy(playlistFilename, tempPlaylistName, strlen(tempPlaylistName));
strncpy(tempPlaylistName, playlistFilename, MAX_FILENAME_LENGTH);
strncat(tempPlaylistName, ".", 1);
//decide if this is an aac file or a mpegts file.
//postpone deciding format until later
/* ifmt = av_find_input_format("mpegts");
if (!ifmt)
{
fprintf(stderr, "Could not find MPEG-TS demuxer.\n");
exit(1);
} */
av_log_set_level(AV_LOG_DEBUG);
av_register_all();
ret = avformat_open_input(&ic, inputFilename, NULL, NULL);
if (ret != 0) {
fprintf(stderr, "Could not open input file %s. Error %d.\n", inputFilename, ret);
exit(1);
}
if (avformat_find_stream_info(ic, NULL) < 0) {
fprintf(stderr, "Could not read stream information.\n");
exit(1);
}
oc = avformat_alloc_context();
if (!oc) {
fprintf(stderr, "Could not allocate output context.");
exit(1);
}
video_index = -1;
audio_index = -1;
for (i = 0; i < ic->nb_streams && (video_index < 0 || audio_index < 0); i++) {
switch (ic->streams[i]->codec->codec_type) {
#ifdef USE_OLD_FFMPEG
case CODEC_TYPE_VIDEO:
#else
case AVMEDIA_TYPE_VIDEO:
#endif
video_index = i;
ic->streams[i]->discard = AVDISCARD_NONE;
if (outputStreams & OUTPUT_STREAM_VIDEO)
video_st = add_output_stream(oc, ic->streams[i]);
break;
#ifdef USE_OLD_FFMPEG
case CODEC_TYPE_AUDIO:
#else
case AVMEDIA_TYPE_AUDIO:
#endif
audio_index = i;
ic->streams[i]->discard = AVDISCARD_NONE;
if (outputStreams & OUTPUT_STREAM_AUDIO)
audio_st = add_output_stream(oc, ic->streams[i]);
break;
default:
ic->streams[i]->discard = AVDISCARD_ALL;
break;
}
}
if (video_index == -1) {
fprintf(stderr, "Stream must have video component.\n");
exit(1);
}
//now that we know the audio and video output streams
//we can decide on an output format.
if (outputStreams == OUTPUT_STREAM_AUDIO) {
//the audio output format should be the same as the audio input format
switch (ic->streams[audio_index]->codec->codec_id) {
case CODEC_ID_MP3:
fprintf(stderr, "Setting output audio to mp3.");
strncpy(baseFileExtension, ".mp3", strlen(".mp3"));
ofmt = av_guess_format("mp3", NULL, NULL);
break;
case CODEC_ID_AAC:
fprintf(stderr, "Setting output audio to aac.");
ofmt = av_guess_format("adts", NULL, NULL);
break;
default:
fprintf(stderr, "Codec id %d not supported.\n", ic->streams[audio_index]->id);
}
if (!ofmt) {
fprintf(stderr, "Could not find audio muxer.\n");
exit(1);
}
} else {
ofmt = av_guess_format("mpegts", NULL, NULL);
if (!ofmt) {
fprintf(stderr, "Could not find MPEG-TS muxer.\n");
exit(1);
}
}
oc->oformat = ofmt;
if (outputStreams & OUTPUT_STREAM_VIDEO && oc->oformat->flags & AVFMT_GLOBALHEADER) {
oc->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
/* Deprecated: pass the options to avformat_write_header directly.
if (av_set_parameters(oc, NULL) < 0) {
fprintf(stderr, "Invalid output format parameters.\n");
exit(1);
}
*/
av_dump_format(oc, 0, baseFileName, 1);
//open the video codec only if there is video data
if (video_index != -1) {
if (outputStreams & OUTPUT_STREAM_VIDEO)
codec = avcodec_find_decoder(video_st->codec->codec_id);
else
codec = avcodec_find_decoder(ic->streams[video_index]->codec->codec_id);
if (!codec) {
fprintf(stderr, "Could not find video decoder, key frames will not be honored.\n");
}
if (outputStreams & OUTPUT_STREAM_VIDEO)
ret = avcodec_open2(video_st->codec, codec, NULL);
else
avcodec_open2(ic->streams[video_index]->codec, codec, NULL);
if (ret < 0) {
fprintf(stderr, "Could not open video decoder, key frames will not be honored.\n");
}
}
snprintf(currentOutputFileName, strlen(baseDirName) + strlen(baseFileName) + strlen(baseFileExtension) + 10, "%s%s-%u%s", baseDirName, baseFileName, output_index++, baseFileExtension);
if (avio_open(&oc->pb, currentOutputFileName, URL_WRONLY) < 0) {
fprintf(stderr, "Could not open '%s'.\n", currentOutputFileName);
exit(1);
}
newFile = 1;
int r = avformat_write_header(oc,NULL);
if (r) {
fprintf(stderr, "Could not write mpegts header to first output file.\n");
debugReturnCode(r);
exit(1);
}
//no segment info is written here. This just creates the shell of the playlist file
write_index = !write_index_file(playlistFilename, tempPlaylistName, segmentLength, actual_segment_durations, baseDirName, baseFileName, baseFileExtension, first_segment, last_segment);
do {
AVPacket packet;
decode_done = av_read_frame(ic, &packet);
if (decode_done < 0) {
break;
}
if (av_dup_packet(&packet) < 0) {
fprintf(stderr, "Could not duplicate packet.");
av_free_packet(&packet);
break;
}
//this time is used to check for a break in the segments
// if (packet.stream_index == video_index && (packet.flags & PKT_FLAG_KEY))
// {
// segment_time = (double)video_st->pts.val * video_st->time_base.num / video_st->time_base.den;
// }
#if USE_OLD_FFMPEG
if (packet.stream_index == video_index && (packet.flags & PKT_FLAG_KEY))
#else
if (packet.stream_index == video_index && (packet.flags & AV_PKT_FLAG_KEY))
#endif
{
segment_time = (double) packet.pts * ic->streams[video_index]->time_base.num / ic->streams[video_index]->time_base.den;
}
// else if (video_index < 0)
// {
// segment_time = (double)audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den;
// }
//get the most recent packet time
//this time is used when the time for the final segment is printed. It may not be on the edge of
//of a keyframe!
if (packet.stream_index == video_index)
packet_time = (double) packet.pts * ic->streams[video_index]->time_base.num / ic->streams[video_index]->time_base.den; //(double)video_st->pts.val * video_st->time_base.num / video_st->time_base.den;
else if (outputStreams & OUTPUT_STREAM_AUDIO)
packet_time = (double) audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den;
else
continue;
//start looking for segment splits for videos one half second before segment duration expires. This is because the
//segments are split on key frames so we cannot expect all segments to be split exactly equally.
if (segment_time - prev_segment_time >= segmentLength - 0.5) {
fprintf(stderr, "looking to print index file at time %lf\n", segment_time);
avio_flush(oc->pb);
avio_close(oc->pb);
if (write_index) {
actual_segment_durations[++last_segment] = (unsigned int) rint(segment_time - prev_segment_time);
write_index = !write_index_file(playlistFilename, tempPlaylistName, segmentLength, actual_segment_durations, baseDirName, baseFileName, baseFileExtension, first_segment, last_segment);
fprintf(stderr, "Writing index file at time %lf\n", packet_time);
}
struct stat st;
stat(currentOutputFileName, &st);
output_bytes += st.st_size;
snprintf(currentOutputFileName, strlen(baseDirName) + strlen(baseFileName) + strlen(baseFileExtension) + 10, "%s%s-%u%s", baseDirName, baseFileName, output_index++, baseFileExtension);
if (avio_open(&oc->pb, currentOutputFileName, URL_WRONLY) < 0) {
fprintf(stderr, "Could not open '%s'\n", currentOutputFileName);
break;
}
newFile = 1;
prev_segment_time = segment_time;
}
if (outputStreams == OUTPUT_STREAM_AUDIO && packet.stream_index == audio_index) {
if (newFile && outputStreams == OUTPUT_STREAM_AUDIO) {
//add id3 tag info
//fprintf(stderr, "adding id3tag to file %s\n", currentOutputFileName);
//printf("%lf %lld %lld %lld %lld %lld %lf\n", segment_time, audio_st->pts.val, audio_st->cur_dts, audio_st->cur_pkt.pts, packet.pts, packet.dts, packet.dts * av_q2d(ic->streams[audio_index]->time_base) );
fill_id3_tag((char*) id3_tag, id3_tag_size, packet.dts);
avio_write(oc->pb, id3_tag, id3_tag_size);
avio_write(oc->pb, image_id3_tag, IMAGE_ID3_SIZE);
avio_flush(oc->pb);
newFile = 0;
}
packet.stream_index = 0; //only one stream in audio only segments
ret = av_interleaved_write_frame(oc, &packet);
} else if (outputStreams & OUTPUT_STREAM_VIDEO) {
if (newFile) {
//fprintf(stderr, "New File: %lld %lld %lld\n", packet.pts, video_st->pts.val, audio_st->pts.val);
//printf("%lf %lld %lld %lld %lld %lld %lf\n", segment_time, audio_st->pts.val, audio_st->cur_dts, audio_st->cur_pkt.pts, packet.pts, packet.dts, packet.dts * av_q2d(ic->streams[audio_index]->time_base) );
newFile = 0;
}
if (outputStreams == OUTPUT_STREAM_VIDEO)
ret = av_write_frame(oc, &packet);
else
ret = av_interleaved_write_frame(oc, &packet);
}
if (ret < 0) {
fprintf(stderr, "Warning: Could not write frame of stream.\n");
} else if (ret > 0) {
fprintf(stderr, "End of stream requested.\n");
av_free_packet(&packet);
break;
}
av_free_packet(&packet);
} while (!decode_done);
//make sure all packets are written and then close the last file.
avio_flush(oc->pb);
av_write_trailer(oc);
if (video_st && video_st->codec)
avcodec_close(video_st->codec);
if (audio_st && audio_st->codec)
avcodec_close(audio_st->codec);
for (i = 0; i < oc->nb_streams; i++) {
av_freep(&oc->streams[i]->codec);
av_freep(&oc->streams[i]);
}
avio_close(oc->pb);
av_free(oc);
struct stat st;
stat(currentOutputFileName, &st);
output_bytes += st.st_size;
if (write_index) {
actual_segment_durations[++last_segment] = (unsigned int) rint(packet_time - prev_segment_time);
//make sure that the last segment length is not zero
if (actual_segment_durations[last_segment] == 0)
actual_segment_durations[last_segment] = 1;
write_index_file(playlistFilename, tempPlaylistName, segmentLength, actual_segment_durations, baseDirName, baseFileName, baseFileExtension, first_segment, last_segment);
}
write_stream_size_file(baseDirName, baseFileName, output_bytes * 8 / segment_time);
return 0;
}
What rudimentary debugging steps have you tried? Have you run your code under gdb or a debugger to discover what line it is crashing on? Or at the very least, added print statements to see how far into your program it is getting before the crash?
Start by compiling your source with the "-g" option in the compiler to add debugging symbols. Then run your code under gdb.
valgrind is another useful tool that might help you find where your program is crashing if it's touching invalid memory.

Warning/error from ALSA's pcm_min.c example. Possible problem?

When I compile ALSA's pcm_min.c example with
gcc -Wall -lasound pcm_min.c -o pcm_min
Everything is fine, but running it, I get the white noise as expected, but I also get this warning/error:
Short write (expected 16384, wrote 7616)
Which comes from the last if-statement.
#include <alsa/asoundlib.h>
static char *device = "default"; /* playback device */
snd_output_t *output = NULL;
unsigned char buffer[16*1024]; /* some random data */
int main(void)
{
int err;
unsigned int i;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
for (i = 0; i < sizeof(buffer); i++)
buffer[i] = random() & 0xff;
if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_set_params(handle,
SND_PCM_FORMAT_U8,
SND_PCM_ACCESS_RW_INTERLEAVED,
1,
48000,
1,
500000)) < 0) { /* 0.5sec */
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
for (i = 0; i < 16; i++) {
frames = snd_pcm_writei(handle, buffer, sizeof(buffer));
if (frames < 0)
frames = snd_pcm_recover(handle, frames, 0);
if (frames < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror(err));
break;
}
if (frames > 0 && frames < (long)sizeof(buffer))
printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), frames);
}
snd_pcm_close(handle);
return 0;
}
Can someone see why this warning/error occur?
Hugs,
Louise
The snd_pcm_writei() function might return less than sizeof(buffer) when there's either a signal received or an underrun. In your case, it seems that you're mixing bytes and frames. The last parameter of the call is the number of frames that you have in your buffer. Since you're passing the number of bytes in your buffer instead, you're seeing an underrun.
I was also having some problems with this example. I modified it a bit and now it works.
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
static char *device = "default"; /* playback device */
snd_output_t *output = NULL;
unsigned char buffer[16*1024]; /* some random data */
int main(void)
{
int err;
unsigned int i;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
snd_pcm_uframes_t bufferSize, periodSize;
for (i = 0; i < sizeof(buffer); i++)
buffer[i] = random() & 0xff;
if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_set_params(handle,
SND_PCM_FORMAT_S16_LE,
SND_PCM_ACCESS_RW_INTERLEAVED,
1, //channels
44100, //sample rate
1, //allow resampling
500000) //required latency in us
) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_prepare(handle)) < 0) {
printf("Pcm prepare error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_get_params( handle, &bufferSize, &periodSize )) < 0) {
printf("Pcm get params error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
printf("Buffer size:%d, Period size:%d\n", (int)bufferSize, (int)periodSize);
for (i = 0; i < 16; i++) {
frames = snd_pcm_writei(handle, buffer, periodSize);
if (frames < 0)
frames = snd_pcm_recover(handle, frames, 0);
if (frames < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror(err));
break;
}
if (frames > 0 && frames < (long)periodSize)
printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), frames);
}
snd_pcm_close(handle);
return 0;
}

Resources