From a81b4cc30ee05292bdc3177680e5d7efbf09fa1f Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sat, 6 Apr 2013 12:16:09 +0200 Subject: [PATCH] Initial checkin. --- cubemap.cpp | 122 +++++++++++++++++++++++++++++++++++++ metacube.h | 17 ++++++ remux.cpp | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 310 insertions(+) create mode 100644 cubemap.cpp create mode 100644 metacube.h create mode 100644 remux.cpp diff --git a/cubemap.cpp b/cubemap.cpp new file mode 100644 index 0000000..cbdab18 --- /dev/null +++ b/cubemap.cpp @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include +#include +#include +#include "metacube.h" + +using namespace std; + +class Input { +public: + Input(); + void curl_callback(char *ptr, size_t bytes); + +private: + void process_block(const char *data, uint32_t size, uint32_t flags); + void drop_pending_data(size_t num_bytes); + + // Data we have received but not fully processed yet. + vector pending_data; + + // If starts with a Metacube header, + // this is true. + bool has_metacube_header; +}; + +Input::Input() + : has_metacube_header(false) +{ +} + +void Input::curl_callback(char *ptr, size_t bytes) +{ + pending_data.insert(pending_data.end(), ptr, ptr + bytes); + + for ( ;; ) { + // If we don't have enough data (yet) for even the Metacube header, just return. + if (pending_data.size() < sizeof(metacube_block_header)) { + return; + } + + // Make sure we have the Metacube sync header at the start. + // We may need to skip over junk data (it _should_ not happen, though). + if (!has_metacube_header) { + char *ptr = static_cast( + memmem(pending_data.data(), pending_data.size(), + METACUBE_SYNC, strlen(METACUBE_SYNC))); + if (ptr == NULL) { + // OK, so we didn't find the sync marker. We know then that + // we do not have the _full_ marker in the buffer, but we + // could have N-1 bytes. Drop everything before that, + // and then give up. + drop_pending_data(pending_data.size() - (strlen(METACUBE_SYNC) - 1)); + return; + } else { + // Yay, we found the header. Drop everything (if anything) before it. + drop_pending_data(ptr - pending_data.data()); + has_metacube_header = true; + + // Re-check that we have the entire header; we could have dropped data. + if (pending_data.size() < sizeof(metacube_block_header)) { + return; + } + } + } + + // Now it's safe to read the header. + metacube_block_header *hdr = reinterpret_cast(pending_data.data()); + assert(memcmp(hdr->sync, METACUBE_SYNC, sizeof(hdr->sync)) == 0); + uint32_t size = ntohl(hdr->size); + uint32_t flags = ntohl(hdr->flags); + + // See if we have the entire block. If not, wait for more data. + if (pending_data.size() < sizeof(metacube_block_header) + size) { + return; + } + + process_block(pending_data.data(), size, flags); + + // Consume this block. This isn't the most efficient way of dealing with things + // should we have many blocks, but these routines don't need to be too efficient + // anyway. + pending_data.erase(pending_data.begin(), pending_data.begin() + sizeof(metacube_block_header) + size); + } +} + +void Input::process_block(const char *data, uint32_t size, uint32_t flags) +{ + // TODO: treat it right here + printf("Block: %d bytes, flags=0x%x\n", size, flags); +} + +void Input::drop_pending_data(size_t num_bytes) +{ + if (num_bytes == 0) { + return; + } + fprintf(stderr, "Warning: Dropping %lld junk bytes from stream, maybe it is not a Metacube stream?\n", + (long long)num_bytes); + pending_data.erase(pending_data.begin(), pending_data.begin() + num_bytes); +} + +size_t curl_callback(char *ptr, size_t size, size_t nmemb, void *userdata) +{ + Input *input = static_cast(userdata); + size_t bytes = size * nmemb; + input->curl_callback(ptr, bytes); + return bytes; +} + +int main(int argc, char **argv) +{ + Input input; + CURL *curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "http://gruessi.zrh.sesse.net:4013/"); + // curl_easy_setopt(curl, CURLOPT_URL, "http://www.google.com/"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &input); + curl_easy_perform(curl); +} diff --git a/metacube.h b/metacube.h new file mode 100644 index 0000000..b40a42e --- /dev/null +++ b/metacube.h @@ -0,0 +1,17 @@ +#ifndef _METACUBE_H +#define _METACUBE_H + +/* Definitions for the Metacube protocol, used to communicate with Cubemap. */ + +#include + +#define METACUBE_SYNC "\\o/_metacube_\\o/" /* 16 bytes long. */ +#define METACUBE_FLAGS_HEADER 0x1 + +struct metacube_block_header { + char sync[16]; /* METACUBE_SYNC */ + uint32_t size; /* Network byte order. Does not include header. */ + uint32_t flags; /* Network byte order. METACUBE_FLAGS_*. */ +}; + +#endif /* !defined(_METACUBE_H) */ diff --git a/remux.cpp b/remux.cpp new file mode 100644 index 0000000..41008f0 --- /dev/null +++ b/remux.cpp @@ -0,0 +1,171 @@ +#define __STDC_CONSTANT_MACROS +#include +#include + +extern "C" { +#include +#include +} + +AVOutputFormat *find_output_format(const char *name) +{ + AVOutputFormat *output_fmt = NULL; + while ((output_fmt = av_oformat_next(output_fmt)) != NULL) { + if (strcmp(output_fmt->name, "flv") == 0) { + return output_fmt; + } + } + return NULL; +} + +void copy_stream_settings(AVStream *stream, const AVStream *istream) +{ + AVCodecContext *codec = stream->codec; + + const AVCodecContext *icodec = istream->codec; + + stream->disposition = istream->disposition; + codec->bits_per_raw_sample = icodec->bits_per_raw_sample; + codec->chroma_sample_location = icodec->chroma_sample_location; + + codec->codec_id = icodec->codec_id; + codec->codec_type = icodec->codec_type; +#if 0 + if (codec->codec_tag == 0) { + if (output_ctx->oformat->codec_tag || + av_codec_get_id (output_ctx->oformat->codec_tag, icodec->codec_tag) == codec->codec_id || + av_codec_get_tag(output_ctx->oformat->codec_tag, icodec->codec_id) <= 0) { + codec->codec_tag = icodec->codec_tag; + } + } +#endif + + codec->bit_rate = icodec->bit_rate; + codec->rc_max_rate = icodec->rc_max_rate; + codec->rc_buffer_size = icodec->rc_buffer_size; + codec->field_order = icodec->field_order; + + uint64_t extra_size = (uint64_t)icodec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE; + codec->extradata = (uint8_t *)av_mallocz(extra_size); + memcpy(codec->extradata, icodec->extradata, icodec->extradata_size); + codec->extradata_size= icodec->extradata_size; + + codec->bits_per_coded_sample = icodec->bits_per_coded_sample; + codec->time_base = istream->time_base; + + if (av_q2d(icodec->time_base)*icodec->ticks_per_frame > av_q2d(istream->time_base) + && av_q2d(istream->time_base) < 1.0/500) { + codec->time_base = icodec->time_base; + codec->time_base.num *= icodec->ticks_per_frame; + } + + av_reduce(&codec->time_base.num, &codec->time_base.den, + codec->time_base.num, codec->time_base.den, INT_MAX); + + printf("time base: %d/%d\n", codec->time_base.num, codec->time_base.den); + + switch (codec->codec_type) { + case AVMEDIA_TYPE_AUDIO: + printf("audio stream\n"); + codec->channel_layout = icodec->channel_layout; + codec->sample_rate = icodec->sample_rate; + codec->channels = icodec->channels; + codec->frame_size = icodec->frame_size; + codec->audio_service_type = icodec->audio_service_type; + codec->block_align = icodec->block_align; +#if 0 + if((codec->block_align == 1 || codec->block_align == 1152) && codec->codec_id == AV_CODEC_ID_MP3) + codec->block_align= 0; + if(codec->codec_id == AV_CODEC_ID_AC3) + codec->block_align= 0; +#endif + break; + case AVMEDIA_TYPE_VIDEO: + printf("video stream\n"); + codec->pix_fmt = icodec->pix_fmt; + codec->width = icodec->width; + codec->height = icodec->height; + codec->has_b_frames = icodec->has_b_frames; + if (codec->sample_aspect_ratio.num == 0) { + if (istream->sample_aspect_ratio.num != 0) { + stream->sample_aspect_ratio = istream->sample_aspect_ratio; + } else if (istream->codec->sample_aspect_ratio.num != 0) { + stream->sample_aspect_ratio = istream->codec->sample_aspect_ratio; + } else { + stream->sample_aspect_ratio = (AVRational){0, 1}; + } + codec->sample_aspect_ratio = stream->sample_aspect_ratio; + } + stream->avg_frame_rate = istream->avg_frame_rate; + break; + case AVMEDIA_TYPE_SUBTITLE: + codec->width = icodec->width; + codec->height = icodec->height; + break; + case AVMEDIA_TYPE_DATA: + case AVMEDIA_TYPE_ATTACHMENT: + break; + default: + abort(); + } +} + +int main(int argc, char *argv[]) +{ + av_register_all(); + avformat_network_init(); + + // Open input. + AVFormatContext *input_ctx = NULL; + if (avformat_open_input(&input_ctx, argv[1], NULL, NULL) != 0) { + exit(1); + } + if (avformat_find_stream_info(input_ctx, NULL) < 0) { + exit(1); + } + av_dump_format(input_ctx, 0, argv[1], 0); + + // Open output. + AVFormatContext *output_ctx = avformat_alloc_context(); + if (output_ctx == NULL) { + exit(1); + } + + output_ctx->oformat = find_output_format("flv"); + if (output_ctx->oformat == NULL) { + printf("Couldn't find output format 'flv'!\n"); + exit(1); + } + + if (avio_open2(&output_ctx->pb, "output.flv", AVIO_FLAG_WRITE, NULL, NULL) != 0) { + printf("Couldn't open output.flv\n"); + exit(1); + } + + // Copy the stream headers from input to output. + printf("Found %d stream(s).\n", input_ctx->nb_streams); + for (int i = 0; i < input_ctx->nb_streams; ++i) { + AVStream *stream = avformat_new_stream(output_ctx, NULL); + if (stream == NULL) { + printf("Couldn't add stream %d\n", i); + exit(1); + } + + copy_stream_settings(stream, input_ctx->streams[i]); + } + if (avformat_write_header(output_ctx, NULL) != 0) { + printf("avformat_write_header()\n"); + exit(1); + } + + + AVPacket packet; + while (av_read_frame(input_ctx, &packet) >= 0) { +// printf("pts=%ld dts=%ld stream=%d size=%d flags=0x%x\n", +// packet.pts, packet.dts, packet.stream_index, packet.size, packet.flags); + av_write_frame(output_ctx, &packet); +// av_free_packet(&packet); + } + + return 0; +} -- 2.39.2