1/* 2 * MJPEG/AVI1 to JPEG/JFIF bitstream format filter 3 * Copyright (c) 2010 Adrian Daerr and Nicolas George 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * FFmpeg is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with FFmpeg; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22/* 23 * Adapted from mjpeg2jpeg.c, with original copyright: 24 * Paris 2010 Adrian Daerr, public domain 25 */ 26 27#include <string.h> 28 29#include "libavutil/error.h" 30#include "libavutil/intreadwrite.h" 31 32#include "bsf.h" 33#include "bsf_internal.h" 34#include "jpegtables.h" 35#include "mjpeg.h" 36 37static const uint8_t jpeg_header[] = { 38 0xff, 0xd8, // SOI 39 0xff, 0xe0, // APP0 40 0x00, 0x10, // APP0 header size (including 41 // this field, but excluding preceding) 42 0x4a, 0x46, 0x49, 0x46, 0x00, // ID string 'JFIF\0' 43 0x01, 0x01, // version 44 0x00, // bits per type 45 0x00, 0x00, // X density 46 0x00, 0x00, // Y density 47 0x00, // X thumbnail size 48 0x00, // Y thumbnail size 49}; 50 51static const int dht_segment_size = 420; 52static const uint8_t dht_segment_head[] = { 0xFF, 0xC4, 0x01, 0xA2, 0x00 }; 53static const uint8_t dht_segment_frag[] = { 54 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 55 0x0a, 0x0b, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 56 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 57}; 58 59static uint8_t *append(uint8_t *buf, const uint8_t *src, int size) 60{ 61 memcpy(buf, src, size); 62 return buf + size; 63} 64 65static uint8_t *append_dht_segment(uint8_t *buf) 66{ 67 buf = append(buf, dht_segment_head, sizeof(dht_segment_head)); 68 buf = append(buf, ff_mjpeg_bits_dc_luminance + 1, 16); 69 buf = append(buf, dht_segment_frag, sizeof(dht_segment_frag)); 70 buf = append(buf, ff_mjpeg_val_dc, 12); 71 *(buf++) = 0x10; 72 buf = append(buf, ff_mjpeg_bits_ac_luminance + 1, 16); 73 buf = append(buf, ff_mjpeg_val_ac_luminance, 162); 74 *(buf++) = 0x11; 75 buf = append(buf, ff_mjpeg_bits_ac_chrominance + 1, 16); 76 buf = append(buf, ff_mjpeg_val_ac_chrominance, 162); 77 return buf; 78} 79 80static int mjpeg2jpeg_filter(AVBSFContext *ctx, AVPacket *out) 81{ 82 AVPacket *in; 83 int ret = 0; 84 int input_skip, output_size; 85 uint8_t *output; 86 87 ret = ff_bsf_get_packet(ctx, &in); 88 if (ret < 0) 89 return ret; 90 91 if (in->size < 12) { 92 av_log(ctx, AV_LOG_ERROR, "input is truncated\n"); 93 ret = AVERROR_INVALIDDATA; 94 goto fail; 95 } 96 if (AV_RB16(in->data) != 0xffd8) { 97 av_log(ctx, AV_LOG_ERROR, "input is not MJPEG\n"); 98 ret = AVERROR_INVALIDDATA; 99 goto fail; 100 } 101 if (in->data[2] == 0xff && in->data[3] == APP0) { 102 input_skip = (in->data[4] << 8) + in->data[5] + 4; 103 } else { 104 input_skip = 2; 105 } 106 if (in->size < input_skip) { 107 av_log(ctx, AV_LOG_ERROR, "input is truncated\n"); 108 ret = AVERROR_INVALIDDATA; 109 goto fail; 110 } 111 output_size = in->size - input_skip + 112 sizeof(jpeg_header) + dht_segment_size; 113 ret = av_new_packet(out, output_size); 114 if (ret < 0) 115 goto fail; 116 117 output = out->data; 118 119 output = append(output, jpeg_header, sizeof(jpeg_header)); 120 output = append_dht_segment(output); 121 output = append(output, in->data + input_skip, in->size - input_skip); 122 123 ret = av_packet_copy_props(out, in); 124 if (ret < 0) 125 goto fail; 126 127fail: 128 if (ret < 0) 129 av_packet_unref(out); 130 av_packet_free(&in); 131 return ret; 132} 133 134static const enum AVCodecID codec_ids[] = { 135 AV_CODEC_ID_MJPEG, AV_CODEC_ID_NONE, 136}; 137 138const FFBitStreamFilter ff_mjpeg2jpeg_bsf = { 139 .p.name = "mjpeg2jpeg", 140 .p.codec_ids = codec_ids, 141 .filter = mjpeg2jpeg_filter, 142}; 143