FFmpeg: unspecified pixel format when opening video with custom context - c

I am trying to decode a video with a custom context. The purpose is that I want to decode the video directly from memory. In the following code, I am reading from file in the read function passed to avio_alloc_context - but this is just for testing purposes.
I think I've read any post there is on Stackoverflow or on any other website related to this topic. At least I definitely tried my best to do so. While there is much in common, the details differ: people set different flags, some say av_probe_input_format is required, some say it isn't, etc. And for some reason nothing works for me.
My problem is that the pixel format is unspecified (see output below), which is why I run into problems later when calling sws_getContext. I checked pFormatContext->streams[videoStreamIndex]->codec->pix_fmt, and it is -1.
Please note my comments // things I tried and // seems not to help in the code. I think, the answer might be hidden somehwere there. I tried many combinations of hints that I've read so far, but I am missing a detail I guess.
The problem is not the video file, because when I go the standard way and just call avformat_open_input(&pFormatContext, pFilePath, NULL, NULL) without a custom context, everything runs fine.
The code compiles and runs as is.
#include <libavformat/avformat.h>
#include <string.h>
#include <stdio.h>
FILE *f;
static int read(void *opaque, uint8_t *buf, int buf_size) {
if (feof(f)) return -1;
return fread(buf, 1, buf_size, f);
}
int openVideo(const char *pFilePath) {
const int bufferSize = 32768;
int ret;
av_register_all();
f = fopen(pFilePath, "rb");
uint8_t *pBuffer = (uint8_t *) av_malloc(bufferSize + AVPROBE_PADDING_SIZE);
AVIOContext *pAVIOContext = avio_alloc_context(pBuffer, bufferSize, 0, NULL,
&read, NULL, NULL);
if (!f || !pBuffer || !pAVIOContext) {
printf("error: open / alloc failed\n");
// cleanup...
return 1;
}
AVFormatContext *pFormatContext = avformat_alloc_context();
pFormatContext->pb = pAVIOContext;
const int readBytes = read(NULL, pBuffer, bufferSize);
printf("readBytes = %i\n", readBytes);
if (readBytes <= 0) {
printf("error: read failed\n");
// cleanup...
return 2;
}
if (fseek(f, 0, SEEK_SET) != 0) {
printf("error: fseek failed\n");
// cleanup...
return 3;
}
// required for av_probe_input_format
memset(pBuffer + readBytes, 0, AVPROBE_PADDING_SIZE);
AVProbeData probeData;
probeData.buf = pBuffer;
probeData.buf_size = readBytes;
probeData.filename = "";
probeData.mime_type = NULL;
pFormatContext->iformat = av_probe_input_format(&probeData, 1);
// things I tried:
//pFormatContext->flags = AVFMT_FLAG_CUSTOM_IO;
//pFormatContext->iformat->flags |= AVFMT_NOFILE;
//pFormatContext->iformat->read_header = NULL;
// seems not to help (therefore commented out here):
AVDictionary *pDictionary = NULL;
//av_dict_set(&pDictionary, "analyzeduration", "8000000", 0);
//av_dict_set(&pDictionary, "probesize", "8000000", 0);
if ((ret = avformat_open_input(&pFormatContext, "", NULL, &pDictionary)) < 0) {
char buffer[4096];
av_strerror(ret, buffer, sizeof(buffer));
printf("error: avformat_open_input failed: %s\n", buffer);
// cleanup...
return 4;
}
printf("retrieving stream information...\n");
if ((ret = avformat_find_stream_info(pFormatContext, NULL)) < 0) {
char buffer[4096];
av_strerror(ret, buffer, sizeof(buffer));
printf("error: avformat_find_stream_info failed: %s\n", buffer);
// cleanup...
return 5;
}
printf("nb_streams = %i\n", pFormatContext->nb_streams);
// further code...
// cleanup...
return 0;
}
int main() {
openVideo("video.mp4");
return 0;
}
This is the output that I get:
readBytes = 32768
retrieving stream information...
[mov,mp4,m4a,3gp,3g2,mj2 # 0xdf8d20] stream 0, offset 0x30: partial file
[mov,mp4,m4a,3gp,3g2,mj2 # 0xdf8d20] Could not find codec parameters for stream 0 (Video: h264 (avc1 / 0x31637661), none, 640x360, 351 kb/s): unspecified pixel format
Consider increasing the value for the 'analyzeduration' and 'probesize' options
nb_streams = 2
UPDATE:
Thanks to WLGfx, here is the solution: The only thing that was missing was the seek function. Apparently, implementing it is mandatory for decoding. It is important to return the new offset - and not 0 in case of success (some solutions found in the web just return the return value of fseek, and that is wrong). Here is the minimal solution that made it work:
static int64_t seek(void *opaque, int64_t offset, int whence) {
if (whence == SEEK_SET && fseek(f, offset, SEEK_SET) == 0) {
return offset;
}
// handling AVSEEK_SIZE doesn't seem mandatory
return -1;
}
Of course, the call to avio_alloc_context needs to be adapted accordingly:
AVIOContext *pAVIOContext = avio_alloc_context(pBuffer, bufferSize, 0, NULL,
&read, NULL, &seek);

Seeing as yours is a file based stream then it is seekable so you can provide the AVIO seek when creating the AVIOContext:
avioContext = avio_alloc_context((uint8_t *)avio_buffer, AVIO_QUEUE_SIZE * PKT_SIZE7,
0,
this, // *** This is your data pointer to a class or other data passed to the callbacks
avio_ReadFunc,
NULL,
avio_SeekFunc);
Handle the seeking with this callback: (You can cast ptr to your class or other data structure)
int64_t FFIOBufferManager::avio_SeekFunc(void *ptr, int64_t pos64, int whence) {
// SEEK_SET(0), SEEK_CUR(1), SEEK_END(2), AVSEEK_SIZE
// ptr is cast to your data or class
switch (whence) {
case 0 : // SEEK_SET
... etc
case (AVSEEK_SIZE) : // get size
return -1; // if you're unable to get the size
break;
}
// set new position in the file
return (int64_t)new_pos; // new position
}
You can also define the codec and the probesize when attaching the AVIOContext to the AVFormatContext. This allows ffmpeg to seek in the stream to better determine the format.
context->pb = ffio->avioContext;
context->flags = AVFMT_FLAG_CUSTOM_IO;
context->iformat = av_find_input_format("mpegts"); // not necessary
context->probesize = 1200000;
So far I haven't had the need for av_probe_input_format, but then again my streams are mpegts.
Hope this helps.
EDIT: Added a comment to the avio_alloc_context function to mention how the ptr is used in the callbacks.

Although the seek was the right answer in your situation, the fact is in my case it's not possible because I have to stream the data and in that situation a seek is just not possible.
So I had to look into: why is a seek required?
From what the ffmpeg docs say, they will cache some data so that way they can seek back if required by the current encoder/decoder. But that buffer is relatively small (you probably don't want to cache 100's of Mb of data).
The fact is that MP4 saves some metadata at the end of the file (once it's known). When reading that format, the decoder wants to seek to a position really very far in the file (near the end) and read what is called the moov atom. Without that info, the system doesn't want to decompress your data.
What I had to do to fix this issue is move that moov atom with the following command:
ffmpeg -i far.mp4 -c copy -map 0 -movflags +faststart close.mp4
faststart means you do not have to stream the entire file to start playing (decoding) the file.

Related

How to write a file to a buffer memory in C

So i got my function here that works to write back any file
int write_file(FILE *f_write) {
// Temp variables
FILE *img = fopen("test.pdf", "wb");
unsigned char buffer[255];
while ( (bytes_read = fread(buffer, 1, sizeof(buffer), f_write) ) > 0) {
fwrite(buffer, 1, bytes_read, img);
}
fclose(img);
return 1;
}
So this works perfecly ive tried with pnj / pdf / jpg etc..
But now i want to stock what ive writen in the memory so i can use it later and not write right away
like an array of uint8_t (maybe) that will contain all the bytes ive writen and that i can send later with sockets to my server and store the file
no idea how to do it
Or maybe i'm making it too complicated and i can just
send(client_socket, FILE, sizeof(FILE), 0); ?
One way to do it would be to create a buffer that exactly fits the size of the file.
In order to do so, you can write a function to get the size of an openned file like so:
size_t get_file_size(FILE *f)
{
size_t pos = ftell(f); // store the cursor position
size_t size;
// go to the end of the file and get the cursor position
fseek(f, 0L, SEEK_END);
size = ftell(f);
// go back to the old position
fseek(f, pos, SEEK_SET);
return size;
}
Then create and fill your buffer:
FILE *f = fopen("your_file", "r");
size_t size = get_file_size(f);
char *buffer = malloc(size);
if (fread(buffer, 1, size, f) != size) { // bytes read != computed file size
// error handling
}
// use your buffer...
// don't forget to free and fclose
free(buffer);
fclose(f);
It is worth mentioning that you should check if the file was opened correctly, and to check if you have enough memory to store the buffer (the one created with malloc).
Edit:
As Andrew Henle said, fseek()/ftell() to get the size of a file is non-portable. Instead, to get the size of your file, you should use one of these techniques depending on your OS (assuming you are trying to open a 'normal' file):
On Linux / MacOS:
#include <sys/stat.h>
struct stat st;
size_t size;
if (stat("your_file", &st) != 0) {
// error handling...
}
size = st.st_size;
On Windows (as answered here) :
__int64 FileSize(const wchar_t* name)
{
HANDLE hFile = CreateFile(name, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return -1; // error condition, could call GetLastError to find out more
LARGE_INTEGER size;
if (!GetFileSizeEx(hFile, &size)) {
CloseHandle(hFile);
return -1; // error condition, could call GetLastError to find out more
}
CloseHandle(hFile);
return size.QuadPart;
}

Why can I not mmap /proc/self/maps?

To be specific: why can I do this:
FILE *fp = fopen("/proc/self/maps", "r");
char buf[513]; buf[512] = NULL;
while(fgets(buf, 512, fp) > NULL) printf("%s", buf);
but not this:
int fd = open("/proc/self/maps", O_RDONLY);
struct stat s;
fstat(fd, &s); // st_size = 0 -> why?
char *file = mmap(0, s.st_size /*or any fixed size*/, PROT_READ, MAP_PRIVATE, fd, 0); // gives EINVAL for st_size (because 0) and ENODEV for any fixed block
write(1, file, st_size);
I know that /proc files are not really files, but it seems to have some defined size and content for the FILE* version. Is it secretly generating it on-the-fly for read or something? What am I missing here?
EDIT:
as I can clearly read() from them, is there any way to get the possible available bytes? or am I stuck to read until EOF?
They are created on the fly as you read them. Maybe this would help, it is a tutorial showing how a proc file can be implemented:
https://devarea.com/linux-kernel-development-creating-a-proc-file-and-interfacing-with-user-space/
tl;dr: you give it a name and read and write handlers, that's it. Proc files are meant to be very simple to implement from the kernel dev's point of view. They do not behave like full-featured files though.
As for the bonus question, there doesn't seem to be a way to indicate the size of the file, only EOF on reading.
proc "files" are not really files, they are just streams that can be read/written from, but they contain no pyhsical data in memory you can map to.
https://tldp.org/LDP/Linux-Filesystem-Hierarchy/html/proc.html
As already explained by others, /proc and /sys are pseudo-filesystems, consisting of data provided by the kernel, that does not really exist until it is read – the kernel generates the data then and there. Since the size varies, and really is unknown until the file is opened for reading, it is not provided to userspace at all.
It is not "unfortunate", however. The same situation occurs very often, for example with character devices (under /dev), pipes, FIFOs (named pipes), and sockets.
We can trivially write a helper function to read pseudofiles completely, using dynamic memory management. For example:
// SPDX-License-Identifier: CC0-1.0
//
#define _POSIX_C_SOURCE 200809L
#define _ATFILE_SOURCE
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
/* For example main() */
#include <stdio.h>
/* Return a directory handle for a specific relative directory.
For absolute paths and paths relative to current directory, use dirfd==AT_FDCWD.
*/
int at_dir(const int dirfd, const char *dirpath)
{
if (dirfd == -1 || !dirpath || !*dirpath) {
errno = EINVAL;
return -1;
}
return openat(dirfd, dirpath, O_DIRECTORY | O_PATH | O_CLOEXEC);
}
/* Read the (pseudofile) contents to a dynamically allocated buffer.
For absolute paths and paths relative to current durectory, use dirfd==AT_FDCWD.
You can safely initialize *dataptr=NULL,*sizeptr=0 for dynamic allocation,
or reuse the buffer from a previous call or e.g. getline().
Returns 0 with errno set if an error occurs. If the file is empty, errno==0.
In all cases, remember to free (*dataptr) after it is no longer needed.
*/
size_t read_pseudofile_at(const int dirfd, const char *path, char **dataptr, size_t *sizeptr)
{
char *data;
size_t size, have = 0;
ssize_t n;
int desc;
if (!path || !*path || !dataptr || !sizeptr) {
errno = EINVAL;
return 0;
}
/* Existing dynamic buffer, or a new buffer? */
size = *sizeptr;
if (!size)
*dataptr = NULL;
data = *dataptr;
/* Open pseudofile. */
desc = openat(dirfd, path, O_RDONLY | O_CLOEXEC | O_NOCTTY);
if (desc == -1) {
/* errno set by openat(). */
return 0;
}
while (1) {
/* Need to resize buffer? */
if (have >= size) {
/* For pseudofiles, linear size growth makes most sense. */
size = (have | 4095) + 4097 - 32;
data = realloc(data, size);
if (!data) {
close(desc);
errno = ENOMEM;
return 0;
}
*dataptr = data;
*sizeptr = size;
}
n = read(desc, data + have, size - have);
if (n > 0) {
have += n;
} else
if (n == 0) {
break;
} else
if (n == -1) {
const int saved_errno = errno;
close(desc);
errno = saved_errno;
return 0;
} else {
close(desc);
errno = EIO;
return 0;
}
}
if (close(desc) == -1) {
/* errno set by close(). */
return 0;
}
/* Append zeroes - we know size > have at this point. */
if (have + 32 > size)
memset(data + have, 0, 32);
else
memset(data + have, 0, size - have);
errno = 0;
return have;
}
int main(void)
{
char *data = NULL;
size_t size = 0;
size_t len;
int selfdir;
selfdir = at_dir(AT_FDCWD, "/proc/self/");
if (selfdir == -1) {
fprintf(stderr, "/proc/self/ is not available: %s.\n", strerror(errno));
exit(EXIT_FAILURE);
}
len = read_pseudofile_at(selfdir, "status", &data, &size);
if (errno) {
fprintf(stderr, "/proc/self/status: %s.\n", strerror(errno));
exit(EXIT_FAILURE);
}
printf("/proc/self/status: %zu bytes\n%s\n", len, data);
len = read_pseudofile_at(selfdir, "maps", &data, &size);
if (errno) {
fprintf(stderr, "/proc/self/maps: %s.\n", strerror(errno));
exit(EXIT_FAILURE);
}
printf("/proc/self/maps: %zu bytes\n%s\n", len, data);
close(selfdir);
free(data); data = NULL; size = 0;
return EXIT_SUCCESS;
}
The above example program opens a directory descriptor ("atfile handle") to /proc/self. (This way you do not need to concatenate strings to construct paths.)
It then reads the contents of /proc/self/status. If successful, it displays its size (in bytes) and its contents.
Next, it reads the contents of /proc/self/maps, reusing the previous buffer. If successful, it displays its size and contents as well.
Finally, the directory descriptor is closed as it is no longer needed, and the dynamically allocated buffer released.
Note that it is perfectly safe to do free(NULL), and also to discard the dynamic buffer (free(data); data=NULL; size=0;) between the read_pseudofile_at() calls.
Because pseudofiles are typically small, the read_pseudofile_at() uses a linear dynamic buffer growth policy. If there is no previous buffer, it starts with 8160 bytes, and grows it by 4096 bytes afterwards until sufficiently large. Feel free to replace it with whatever growth policy you prefer, this one is just an example, but works quite well in practice without wasting much memory.

Send a File with socket in C for Linux

I'm writing a small and simple server (in C language for Linux stations).
A client requests a file to my server, my server asks this file to another server which sends it to my server.
My server should NOT receive ALL the file before sending it to the client BUT must send the bytes of the file so as they arrive.
This is an exercise in school so I can not dissociate myself from this requirement.
I have implemented the function explained below. The problem is that the client receives a non-deterministic number of bytes and NEVER the entire file.
int Recv_and_send_file (int socketa, int socketb, char *buffer, size_t file_size){
size_t n;
ssize_t nread;
ssize_t nwritten;
char c;
for (n=1; n<file_size; n++)
{
nread=recv(socketa, &c, 1, 0);
if (nread == 1)
{
nwritten = send(socketb,&c,1,0);
}
else if (nread == 0)
{
*buffer = 0;
return (-1); /* Errore */
}
else
return (-1); /* Errore */
}
}
*buffer = 0;
return (n);
}
Someone could kindly tell me where I'm wrong?
Is it an stupid idea to change the values ​​SO_SNDBUF and SO_RCVBUF on both the server and the client?
Assuming the file_size is the total number of bytes you want to send, then your for loop will only send file_size - 1 bytes. In other words, you are off by one. Start from 0 instead to fix this:
for (n=0; n<file_size; n++)
{ //..
You capture the return value of send(), but you do not check to see if it was successful or not.
You are treating a 0 return value from recv() the same as an error. Since you do not show what you do after returning -1 from your function, I don't know if this may be contributing to your problem or not.
Certain errors on send() and recv() are "soft", in that you are allowed to retry the operation for those particular errors. One such error is EINTR, but check the documentation on your system to see if there are others.
In order to optimize performance and simplify your code, you can use splice()+pipes. Sendfile enables you to "forward" data between file descriptors, without the copy to user space.
Are you sure you have copied the correct code? That part as it is would not compile, there is a } in the last else which don't match with a corresponding {.
Also, how you get to know the file size? if it's send thru the socket as an integer, bear in mind the possible byte order of the source and destination machines.
Anyway, you are reading one byte at a time, you should improve it this way:
EDIT: use buffer and not the extra buff[2048];
int Recv_and_send_file (int socketa, int socketb, char *buffer, size_t file_size){
ssize_t nread;
ssize_t nwritten;
ssize_t bLeft=file_size;
while (bLeft > 0)
{
nread=recv(socketa, buffer, bleft, 0);
if (nread > 0)
{
nwritten = send(socketb, buffer, nread, 0);
bLeft -= nread;
buffer+=nread;
}
else if (nread == 0)
{
// I think this could raise a memory exception, read below
*buffer = 0;
return (-1); /* Errore */
}
else
{
return (-1); /* Errore */
}
}
// If buffer is allocated with file_size bytes this one will raise a memory exception
// *buffer = 0;
return (file_size-bLeft);
}

How to Search for New Lines while Reading from a File in C/C++

I am implementing my own version of the ("cat") command in Unix for practice. After i did that i became interested in implementing some flags like (-n) and (-b).
My Question: I am looking for a way to locate the blank and new lines while reading from my file. I can't remember what library or function i should use.
Here is the source code I am working on:
#include <fcntl.h>
#include <unistd.h>
static int cat_fd(int fd)
{
char buf[4096];
ssize_t nread;
while ((nread = read(fd, buf, sizeof buf)) > 0)
{
ssize_t ntotalwritten = 0;
while (ntotalwritten < nread)
{
ssize_t nwritten = write(STDOUT_FILENO, buf + ntotalwritten, nread - ntotalwritten);
if (nwritten < 1)
{
return -1;
}
ntotalwritten += nwritten;
}
}
return (nread == 0) ? 0 : -1;
}
static int cat(const char *fname)
{
int fd, success;
if ((fd = open(fname, O_RDONLY)) == -1)
{
return -1;
}
success = cat_fd(fd);
if (close(fd) != 0)
{
return -1;
}
return success;
}
int main(int argc, char **argv)
{
int i;
if (argc == 1)
{
if (cat_fd(STDIN_FILENO) != 0)
goto error;
}
else
{
for (i = 1; i < argc; i++)
{
if (cat(argv[i]) != 0)
{
goto error;
}
}
}
return 0;
error:
write(STDOUT_FILENO, "error\n", 6);
return 1;
}
Any ideas or suggestions concerning my question are greatly appreciated.
I would be even more grateful if you can type for me the complete function prototype that i shall be using as i am not an experienced programmer.
Thanks in advance for your help.
P.S: I am implementing the (-n) and (-b) flags. Thus, i am looking forward to write the line number at the beginning of each line in the file that i am reading.
While there is a function that does line-based file input in C (it's called fgets), you can't really use it for cat, because:
There's no way to know the maximum length of the line beforehand;
You'll lose portions of the input if it contains null bytes.
You'll have to look for newline symbols in your buffer after you read it, and once you find any, print the prefix of the buffer, followed by newline, line number, and the rest of the buffer (with additional processing of remaining newlines, of course).
An easier solution would be to switch to processing input one byte at a time; you can use FILE* and fgetc to use CRT-provided buffering so that you don't actually do a syscall for each read/write, or read file in blocks as you do now, and do byte processing inside the loop. Then it's a matter of writing a state machine - if a previous read character was a newline, then output a line number, unless this character is a newline and -b option is used, etc.
This still results in a less efficient solution, so you may want to treat cat without arguments specially - i.e. switch to byte-per-byte processing only if you need it. In fact, this is exactly what at least one of actual cat implementations does.
I recall reading that cat memory maps files for fast execution. Use mmap(2).
http://kernel.org/doc/man-pages/online/pages/man2/munmap.2.html
I found this example: http://ladweb.net/src/map-cat.c
I know this doesn't answer your question about newlines. I guess
memchr() would do the trick.

how to send an image in winsock2, using c

I am writing a very simple webserver in c (winsock2).
I am able to return the contents of my html pages.
Currently, what I am doing is writing the contents of a file into a char* buffer and sending it using "send()"
Although when I try to read an image (jpg, bmp), I can't write the characters into a buffer a some characters are "null" (0).
How can I send a whole image file ?
Thanks.
You can store null character in a char* buffer. You just have to use a counter to remember how many characters were written, instead of recomputing it by counting number of non-null characters (this can either be an integer or a pointer to the next point of insertion in the buffer).
To send a file, you'll do something like that:
int sendFile(int sock, const char* filename) {
FILE* file = fopen(filename, "rb");
if (file == NULL)
return -1;
if (fseek(file, 0, SEEK_END) != 0) {
fclose(file);
return -1;
}
off_t size = ftello(file);
if (fseek(file, 0, SEEK_SET) != 0) {
fclose(file);
return -1;
}
if (SendBinaryFileHeaderAndSize(sock, size) < 0) {
fclose(file);
return -1;
}
char buffer[4096];
for (;;) {
size_t read = fread(buffer, 1, sizeof(buffer), file);
if (read == 0) {
int retcode = 0;
if (ferror(file))
retcode = -1;
fclose(file);
return retcode;
}
for (size_t sent = 0; sent < read;) {
int ret = send(sock, buffer + sent, read - sent, 0);
if (ret < 0) {
fclose(file);
return -1;
}
assert(ret <= read - sent);
sent += ret;
}
}
}
You need to understand how send() and fread() work. 0s in the buffer are not a problem for send or fread - they do not interpret their buffers as null-terminated strings.
Depending on how you load the image into your webserver, you would need to use either Winsock:TransmitPackets or Winsock:TransmitFile, also also wrapping the image in the appropriate HTTP headers
Note that these are MS specific extensions.
Also see c++ - Bitmap transfer using winsock getdibits and setdibits

Resources