Stream Filter Problems

This forum is about all development around libVLC.
j_vlc
Blank Cone
Blank Cone
Posts: 16
Joined: 11 Mar 2013 13:03

Stream Filter Problems

Postby j_vlc » 01 Apr 2013 14:13

Hi! I'm making an stream filter plugin to view AES encrypted files. I see that this kind of plugin is undocumented in the wiki, but I'm looking the source code of the existing plugins.

For the moment I'm making a test file with this command:

Code: Select all

openssl enc -aes-128-cbc -in test.mp3 -out protected.mp3 -K "01020304050607080900010203040506" -iv "01020304050607080900010203040506" && echo -n 'HELLO' | cat - protected.mp3 > temp && mv temp protected.mp3
That generates an AES 128 chunk preceded by "HELLO", to recognize the stream.
For the moment, I'm not decrypting the file in the plugin, instead of this I'm reading the original file (test.mp3 in the example) and returning this content.


Here is my plugin description and my stream_sys_t struct:

Code: Select all

vlc_module_begin() set_shortname("Test") set_description(N_("Plugin to see encrypted files")) set_category(CAT_INPUT) set_subcategory(SUBCAT_INPUT_STREAM_FILTER) set_capability("stream_filter", 20) set_callbacks(callbackOpen, callbackClose) vlc_module_end() struct stream_sys_t { /*! Handler to read contents of the protected file. */ FILE *fileHandler; /*! The size of the file to play. */ uint64_t fileSize; /*! Current offset of the readed data. */ uint64_t peekOffset; uint64_t readOffset; /*! Buffer where save the data and their size. */ uint8_t *buffer; uint64_t bufferLength; };
My open function:

Code: Select all

static int callbackOpen(vlc_object_t *streamObj) { stream_t *stream = (stream_t *)streamObj; const uint8_t *peek; if (stream_Peek(stream->p_source, &peek, 100) < 5) { LOGERROR(streamObj, "stream_Peek fails reading the HELLO mark"); return VLC_EGENERIC; } if (memcmp(peek, "HELLO", 5) != 0) { return VLC_EGENERIC; } else { LOG(streamObj, "File protected detected"); // Open the protected file to be able to get data //FILE *fileHandler = fopen(stream->psz_path, "rb"); /* TODO TEST */ FILE *fileHandler = fopen("/Users/jose/Desktop/test.mp3", "rb"); if (!fileHandler) { LOGERROR(streamObj, "Can not open protected file"); return VLC_EGENERIC; } // Set the stream p_sys stream_sys_t *info = (stream_sys_t *)malloc(sizeof(*info)); info->fileHandler = fileHandler; info->peekOffset = 0; info->readOffset = 0; info->buffer = NULL; info->bufferLength = 0; fseek(fileHandler, 0, SEEK_END); info->fileSize = ftell(fileHandler); fseek(fileHandler, 0, SEEK_SET); stream->p_sys = info; // Set the callbacks stream->pf_read = callbackRead; stream->pf_peek = callbackPeek; stream->pf_control = callbackControl; // Init the mutex vlc_mutex_init(&mutex); running = true; return VLC_SUCCESS; } }
And there are my callbacks:

Code: Select all

static int callbackRead(stream_t *stream, void *readBuffer, unsigned int readBufferLength) { stream_sys_t *info = stream->p_sys; uint64_t offset = (info->readOffset == 0) ? info->peekOffset : info->readOffset; if (offset >= info->fileSize) { LOG(stream, "##################### EOF #####################"); vlc_mutex_unlock(&mutex); return 0; } size_t bytesToRead = (offset + readBufferLength > info->fileSize) ? info->fileSize - offset : readBufferLength; info->readOffset += offset + bytesToRead; // Caller skips data if (readBuffer == NULL) { LOG(stream, "\treadBuffer == NULL"); const uint8_t *nothing; callbackPeek(stream, &nothing, readBufferLength); return readBufferLength; } info->readOffset += offset + bytesToRead; fseek(info->fileHandler, PROTON_MARK_LENGTH+offset, SEEK_SET); fread(readBuffer, bytesToRead, bytesToRead, info->fileHandler); return bytesToRead; } static int callbackPeek(stream_t *stream, const uint8_t **returnBuffer, unsigned int returnBufferLength) { stream_sys_t *info = stream->p_sys; if (info->buffer == NULL) { info->buffer = (uint8_t *)malloc(returnBufferLength); info->bufferLength = returnBufferLength; } else if (info->bufferLength < returnBufferLength) { uint8_t *tempBuffer = (uint8_t *)malloc(returnBufferLength); memcpy(tempBuffer, info->buffer, info->bufferLength); free(info->buffer); info->buffer = tempBuffer; info->bufferLength = returnBufferLength; } size_t bytesToRead = (returnBufferLength > info->fileSize) ? info->fileSize : returnBufferLength; fseek(info->fileHandler, PROTON_MARK_LENGTH, SEEK_SET); fread(info->buffer, bytesToRead, bytesToRead, info->fileHandler); info->peekOffset = (info->peekOffset > bytesToRead) ? info->peekOffset : bytesToRead; *returnBuffer = info->buffer; return bytesToRead; }
My problems are...

- My read callback always has readBuffer == NULL
- My peek allocation buffer routines fails randomly on free
- Really I don't understand what is supposed to return in read and peek callbacks... Testing the Gzip plugin, I see that peek always return the first returnBufferLength bytes of the uncompresed file and read returns readBufferLength bytes of the first non peeked chunk of uncompresed data, but this plugin never receives readBuffer == NULL :S

Someone can help me explaining a bit what is supposed to return in this functions please?
Thank you very much in advance!!

j_vlc
Blank Cone
Blank Cone
Posts: 16
Joined: 11 Mar 2013 13:03

Re: Stream Filter Problems

Postby j_vlc » 03 Apr 2013 17:04

After a bunch of hours of testing, here is a minimal but functional stream filter plugin source code:

Code: Select all

#ifdef HAVE_CONFIG_H # include "config.h" #endif #include <vlc_common.h> #include <vlc_plugin.h> #include <vlc_stream.h> static int Open(vlc_object_t *obj); static void Close(vlc_object_t *obj); static int Read(stream_t *stream, void *buf, unsigned int buflen); static int Peek(stream_t *stream, const uint8_t **pbuf, unsigned int len); static int Control(stream_t *stream, int query, va_list args); vlc_module_begin () set_category (CAT_INPUT) set_subcategory (SUBCAT_INPUT_STREAM_FILTER) set_capability ("stream_filter", 20) set_description (N_("Description of your module")) set_callbacks (Open, Close) vlc_module_end () struct stream_sys_t { uint8_t *fileBuffer; uint64_t fileSize; uint64_t offset; }; //----------------------------------------------------------------------------------------------------------------------------- /** * Reads decompressed from the decompression program * @return -1 for EAGAIN, 0 for EOF, byte count otherwise. */ static int Read(stream_t *stream, void *buf, unsigned int buflen) { stream_sys_t *p_sys = stream->p_sys; int bytesToReturn = (p_sys->offset + buflen) > p_sys->fileSize ? p_sys->fileSize - p_sys->offset : buflen; if (p_sys->offset == p_sys->fileSize) { return 0; } if (buf == NULL) { /* caller skips data, get big enough peek buffer */ buflen = Peek (stream, &(const uint8_t *){ NULL }, buflen); } else { memcpy(buf, p_sys->fileBuffer+p_sys->offset, bytesToReturn); } p_sys->offset += bytesToReturn; return bytesToReturn; } static int Peek(stream_t *stream, const uint8_t **pbuf, unsigned int len) { stream_sys_t *p_sys = stream->p_sys; *pbuf = p_sys->fileBuffer + p_sys->offset; return len; } static int Control(stream_t *stream, int query, va_list args) { stream_sys_t *p_sys = stream->p_sys; switch (query) { case STREAM_CAN_SEEK: case STREAM_CAN_FASTSEEK: *(va_arg (args, bool *)) = false; break; case STREAM_GET_POSITION: *(va_arg (args, uint64_t *)) = p_sys->offset; break; case STREAM_GET_SIZE: *(va_arg (args, uint64_t *)) = 0; break; default: return VLC_EGENERIC; } return VLC_SUCCESS; } //----------------------------------------------------------------------------------------------------------------------------- static void Close(vlc_object_t *obj) { stream_t *stream = (stream_t *)obj; stream_sys_t *p_sys = stream->p_sys; free(p_sys->fileBuffer); free (p_sys); } static int Open(vlc_object_t *obj) { stream_t *stream = (stream_t *)obj; const uint8_t *peek; if (stream_Peek(stream->p_source, &peek, 5) < 5) return VLC_EGENERIC; if (memcmp(peek, "HEELO", 5) != 0) return VLC_EGENERIC; stream_sys_t *p_sys = stream->p_sys = malloc (sizeof (*p_sys)); if (p_sys == NULL) return VLC_ENOMEM; stream->pf_read = Read; stream->pf_peek = Peek; stream->pf_control = Control; FILE *fileHandler = fopen("/Users/jose/Desktop/fake.mp3", "rb"); if (!fileHandler) return VLC_EGENERIC; fseek(fileHandler, 0, SEEK_END); p_sys->fileSize = ftell(fileHandler); fseek(fileHandler, 0, SEEK_SET); p_sys->fileBuffer = (uint8_t *)malloc(p_sys->fileSize); fread(p_sys->fileBuffer, p_sys->fileSize, 1, fileHandler); fclose(fileHandler); p_sys->offset = 0; return VLC_SUCCESS; }
It basically detects an encrypted file generated as described in my previous post and plays another file, ignoring the input stream, in the example "/Users/jose/Desktop/fake.mp3".
I hope that will be useful for someone.

Rémi Denis-Courmont
Developer
Developer
Posts: 15267
Joined: 07 Jun 2004 16:01
VLC version: master
Operating System: Linux
Contact:

Re: Stream Filter Problems

Postby Rémi Denis-Courmont » 03 Apr 2013 17:28

readBuffer == NULL is a request to skip/discard, IIRC.
Rémi Denis-Courmont
https://www.remlab.net/
Private messages soliciting support will be systematically discarded


Return to “Development around libVLC”

Who is online

Users browsing this forum: No registered users and 23 guests